blob: 1d71bd97bd1f4891765eb338d37496a5eaf99820 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// 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 <utility>
#include <vector>
#include <chromeos/patchpanel/net_util.h>
#include <chromeos/patchpanel/dbus/fake_client.h>
#include <dbus/mock_bus.h>
#include <dbus/mock_object_proxy.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <shill/dbus/client/fake_client.h>
#include <shill/dbus-constants.h>
#include <shill/dbus-proxy-mocks.h>
#include <shill/net/rtnl_handler.h>
using testing::ElementsAreArray;
namespace dns_proxy {
namespace {
constexpr base::TimeDelta kRequestTimeout = base::TimeDelta::FromSeconds(10000);
constexpr base::TimeDelta kRequestRetryDelay =
base::TimeDelta::FromMilliseconds(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);
}
} // namespace
using org::chromium::flimflam::ManagerProxyInterface;
using org::chromium::flimflam::ManagerProxyMock;
using testing::_;
using testing::ByMove;
using testing::DoAll;
using testing::IsEmpty;
using testing::Return;
using testing::SetArgPointee;
using testing::StrEq;
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 FakePatchpanelClient : public patchpanel::FakeClient {
public:
FakePatchpanelClient() = default;
~FakePatchpanelClient() = default;
void SetConnectNamespaceResult(
int fd, const patchpanel::ConnectNamespaceResponse& resp) {
ns_fd_ = fd;
ns_resp_ = resp;
}
std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse>
ConnectNamespace(pid_t pid,
const std::string& outbound_ifname,
bool forward_user_traffic,
bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) override {
ns_ifname_ = outbound_ifname;
ns_rvpn_ = route_on_vpn;
ns_ts_ = traffic_source;
return {base::ScopedFD(ns_fd_), ns_resp_};
}
base::ScopedFD RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::RuleType,
const std::string&,
const std::string&,
const std::vector<std::string>&) override {
return base::ScopedFD(make_fd());
}
std::string ns_ifname_;
bool ns_rvpn_;
patchpanel::TrafficCounter::Source ns_ts_;
int ns_fd_;
patchpanel::ConnectNamespaceResponse ns_resp_;
};
class FakeFeaturesClient : public ChromeFeaturesServiceClient {
public:
explicit FakeFeaturesClient(bool enabled)
: ChromeFeaturesServiceClient(nullptr), enabled_(enabled) {}
void IsDNSProxyEnabled(
ChromeFeaturesServiceClient::IsFeatureEnabledCallback callback) override {
is_dnsproxy_enabled_called = true;
std::move(callback).Run(enabled_);
}
bool is_dnsproxy_enabled_called{false};
private:
bool enabled_;
};
class FakeSessionMonitor : public SessionMonitor {
public:
explicit FakeSessionMonitor(scoped_refptr<dbus::Bus> bus)
: SessionMonitor(bus) {}
void Login() { OnSessionStateChanged("started"); }
void Logout() { OnSessionStateChanged("stopping"); }
};
class MockPatchpanelClient : public patchpanel::Client {
public:
MockPatchpanelClient() = default;
~MockPatchpanelClient() = default;
MOCK_METHOD(void,
RegisterOnAvailableCallback,
(base::RepeatingCallback<void(bool)>),
(override));
MOCK_METHOD(void,
RegisterProcessChangedCallback,
(base::RepeatingCallback<void(bool)>),
(override));
MOCK_METHOD(bool, NotifyArcStartup, (pid_t), (override));
MOCK_METHOD(bool, NotifyArcShutdown, (), (override));
MOCK_METHOD(std::vector<patchpanel::NetworkDevice>,
NotifyArcVmStartup,
(uint32_t),
(override));
MOCK_METHOD(bool, NotifyArcVmShutdown, (uint32_t), (override));
MOCK_METHOD(bool,
NotifyTerminaVmStartup,
(uint32_t, patchpanel::NetworkDevice*, patchpanel::IPv4Subnet*),
(override));
MOCK_METHOD(bool, NotifyTerminaVmShutdown, (uint32_t), (override));
MOCK_METHOD(bool,
NotifyPluginVmStartup,
(uint64_t, int, patchpanel::NetworkDevice*),
(override));
MOCK_METHOD(bool, NotifyPluginVmShutdown, (uint64_t), (override));
MOCK_METHOD(bool, DefaultVpnRouting, (int), (override));
MOCK_METHOD(bool, RouteOnVpn, (int), (override));
MOCK_METHOD(bool, BypassVpn, (int), (override));
MOCK_METHOD((std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse>),
ConnectNamespace,
(pid_t pid,
const std::string& outbound_ifname,
bool forward_user_traffic,
bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source),
(override));
MOCK_METHOD(void,
GetTrafficCounters,
(const std::set<std::string>&, GetTrafficCountersCallback),
(override));
MOCK_METHOD(bool,
ModifyPortRule,
(patchpanel::ModifyPortRuleRequest::Operation,
patchpanel::ModifyPortRuleRequest::RuleType,
patchpanel::ModifyPortRuleRequest::Protocol,
const std::string&,
const std::string&,
uint32_t,
const std::string&,
uint32_t),
(override));
MOCK_METHOD(base::ScopedFD,
RedirectDns,
(patchpanel::SetDnsRedirectionRuleRequest::RuleType,
const std::string&,
const std::string&,
const std::vector<std::string>&),
(override));
MOCK_METHOD(std::vector<patchpanel::NetworkDevice>,
GetDevices,
(),
(override));
MOCK_METHOD(void,
RegisterNetworkDeviceChangedSignalHandler,
(NetworkDeviceChangedSignalHandler),
(override));
MOCK_METHOD(void,
RegisterNeighborReachabilityEventHandler,
(NeighborReachabilityEventHandler),
(override));
MOCK_METHOD(bool, SetVpnLockdown, (bool), (override));
};
class MockResolver : public Resolver {
public:
MockResolver()
: Resolver(kRequestTimeout, kRequestRetryDelay, kRequestMaxRetry) {}
~MockResolver() = 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)
: Proxy(opts, std::move(patchpanel), std::move(shill)) {}
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() { mock_bus_->ShutdownAndBlock(); }
void SetUp() override {
EXPECT_CALL(*mock_bus_, GetObjectProxy(_, _))
.WillRepeatedly(Return(mock_proxy_.get()));
}
std::unique_ptr<FakePatchpanelClient> PatchpanelClient() const {
return std::make_unique<FakePatchpanelClient>();
}
std::unique_ptr<FakeShillClient> ShillClient() const {
return std::make_unique<FakeShillClient>(
mock_bus_, reinterpret_cast<ManagerProxyInterface*>(
const_cast<ManagerProxyMock*>(&mock_manager_)));
}
protected:
scoped_refptr<dbus::MockBus> mock_bus_;
scoped_refptr<dbus::MockObjectProxy> mock_proxy_;
ManagerProxyMock mock_manager_;
};
TEST_F(ProxyTest, SystemProxy_OnShutdownClearsAddressPropertyOnShill) {
EXPECT_CALL(mock_manager_, SetDNSProxyIPv4Address(StrEq(""), _, _))
.WillOnce(Return(true));
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
int unused;
proxy.shill_ready_ = true;
proxy.OnShutdown(&unused);
}
TEST_F(ProxyTest, NonSystemProxy_OnShutdownDoesNotCallShill) {
EXPECT_CALL(mock_manager_, SetDNSProxyIPv4Address(_, _, _)).Times(0);
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, PatchpanelClient(),
ShillClient());
int unused;
proxy.shill_ready_ = true;
proxy.OnShutdown(&unused);
}
TEST_F(ProxyTest, SystemProxy_SetShillPropertyDoesntCrashIfDieFalse) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
EXPECT_CALL(mock_manager_, SetProperty(_, _, _, _)).Times(0);
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.shill_ready_ = true;
proxy.SetShillProperty("10.10.10.10", false, 0);
}
TEST_F(ProxyTest, ShillInitializedWhenReady) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.OnShillReady(true);
EXPECT_TRUE(proxy.shill_ready_);
}
TEST_F(ProxyTest, SystemProxy_ConnectedNamedspace) {
auto pp = PatchpanelClient();
auto* pp_ptr = pp.get();
pp->SetConnectNamespaceResult(make_fd(),
patchpanel::ConnectNamespaceResponse());
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, std::move(pp),
ShillClient());
proxy.OnPatchpanelReady(true);
EXPECT_TRUE(pp_ptr->ns_ifname_.empty());
EXPECT_FALSE(pp_ptr->ns_rvpn_);
EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::SYSTEM);
}
TEST_F(ProxyTest, DefaultProxy_ConnectedNamedspace) {
auto pp = PatchpanelClient();
auto* pp_ptr = pp.get();
pp->SetConnectNamespaceResult(make_fd(),
patchpanel::ConnectNamespaceResponse());
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(pp),
ShillClient());
proxy.OnPatchpanelReady(true);
EXPECT_TRUE(pp_ptr->ns_ifname_.empty());
EXPECT_TRUE(pp_ptr->ns_rvpn_);
EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::USER);
}
TEST_F(ProxyTest, ArcProxy_ConnectedNamedspace) {
auto pp = PatchpanelClient();
auto* pp_ptr = pp.get();
pp->SetConnectNamespaceResult(make_fd(),
patchpanel::ConnectNamespaceResponse());
Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
std::move(pp), ShillClient());
proxy.OnPatchpanelReady(true);
EXPECT_EQ(pp_ptr->ns_ifname_, "eth0");
EXPECT_FALSE(pp_ptr->ns_rvpn_);
EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::ARC);
}
TEST_F(ProxyTest, ShillResetRestoresAddressProperty) {
auto pp = PatchpanelClient();
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
pp->SetConnectNamespaceResult(make_fd(), resp);
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, std::move(pp),
ShillClient());
proxy.OnPatchpanelReady(true);
EXPECT_CALL(mock_manager_, SetDNSProxyIPv4Address(StrEq("10.10.10.10"), _, _))
.WillOnce(Return(true));
proxy.shill_ready_ = true;
proxy.OnShillReset(true);
}
TEST_F(ProxyTest, StateClearedIfDefaultServiceDrops) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.OnDefaultDeviceChanged(nullptr /* no service */);
EXPECT_FALSE(proxy.device_);
EXPECT_FALSE(proxy.resolver_);
}
TEST_F(ProxyTest, ArcProxy_IgnoredIfDefaultServiceDrops) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.OnDefaultDeviceChanged(nullptr /* no service */);
EXPECT_TRUE(proxy.device_);
EXPECT_TRUE(proxy.resolver_);
}
TEST_F(ProxyTest, StateClearedIfDefaultServiceIsNotOnline) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
proxy.resolver_ = std::make_unique<MockResolver>();
shill::Client::Device dev;
dev.state = shill::Client::Device::ConnectionState::kReady;
proxy.OnDefaultDeviceChanged(&dev);
EXPECT_FALSE(proxy.device_);
EXPECT_FALSE(proxy.resolver_);
}
TEST_F(ProxyTest, NewResolverStartsListeningOnDefaultServiceComesOnline) {
TestProxy proxy(Proxy::Options{.type = Proxy::Type::kDefault},
PatchpanelClient(), ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver = std::move(resolver);
shill::Client::Device dev;
dev.state = shill::Client::Device::ConnectionState::kOnline;
EXPECT_CALL(*mock_resolver, ListenUDP(_)).WillOnce(Return(true));
EXPECT_CALL(*mock_resolver, ListenTCP(_)).WillOnce(Return(true));
brillo::VariantDictionary props;
EXPECT_CALL(mock_manager_, GetProperties(_, _, _))
.WillOnce(DoAll(SetArgPointee<0>(props), Return(true)));
proxy.OnDefaultDeviceChanged(&dev);
EXPECT_TRUE(proxy.resolver_);
}
TEST_F(ProxyTest, NameServersUpdatedOnDefaultServiceComesOnline) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::Device dev;
dev.state = shill::Client::Device::ConnectionState::kOnline;
dev.ipconfig.ipv4_dns_addresses = {"8.8.8.8", "8.8.4.4"};
dev.ipconfig.ipv6_dns_addresses = {"2001:4860:4860::8888",
"2001:4860:4860::8844"};
// Doesn't call listen since the resolver already exists.
EXPECT_CALL(*mock_resolver, ListenUDP(_)).Times(0);
EXPECT_CALL(*mock_resolver, ListenTCP(_)).Times(0);
EXPECT_CALL(*mock_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);
}
TEST_F(ProxyTest, SystemProxy_ShillPropertyUpdatedOnDefaultServiceComesOnline) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.shill_ready_ = true;
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::Device dev;
dev.state = shill::Client::Device::ConnectionState::kOnline;
EXPECT_CALL(*mock_resolver, SetNameServers(_));
EXPECT_CALL(mock_manager_, SetDNSProxyIPv4Address(_, _, _))
.WillOnce(Return(true));
proxy.OnDefaultDeviceChanged(&dev);
}
TEST_F(ProxyTest, SystemProxy_IgnoresVPN) {
TestProxy proxy(Proxy::Options{.type = Proxy::Type::kSystem},
PatchpanelClient(), ShillClient());
proxy.shill_ready_ = true;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
ON_CALL(*mock_resolver, ListenUDP(_)).WillByDefault(Return(true));
ON_CALL(*mock_resolver, ListenTCP(_)).WillByDefault(Return(true));
brillo::VariantDictionary props;
EXPECT_CALL(mock_manager_, GetProperties(_, _, _))
.WillOnce(DoAll(SetArgPointee<0>(props), Return(true)));
EXPECT_CALL(mock_manager_, SetDNSProxyIPv4Address(_, _, _))
.WillOnce(Return(true));
proxy.resolver = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::Device dev;
dev.type = shill::Client::Device::Type::kWifi;
dev.state = shill::Client::Device::ConnectionState::kOnline;
proxy.OnDefaultDeviceChanged(&dev);
EXPECT_TRUE(proxy.device_);
EXPECT_EQ(proxy.device_->type, shill::Client::Device::Type::kWifi);
dev.type = shill::Client::Device::Type::kVPN;
proxy.OnDefaultDeviceChanged(&dev);
EXPECT_TRUE(proxy.device_);
EXPECT_EQ(proxy.device_->type, shill::Client::Device::Type::kWifi);
}
TEST_F(ProxyTest, SystemProxy_GetsPhysicalDeviceOnInitialVPN) {
auto shill = ShillClient();
auto* shill_ptr = shill.get();
TestProxy proxy(Proxy::Options{.type = Proxy::Type::kSystem},
PatchpanelClient(), std::move(shill));
proxy.shill_ready_ = true;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
ON_CALL(*mock_resolver, ListenUDP(_)).WillByDefault(Return(true));
ON_CALL(*mock_resolver, ListenTCP(_)).WillByDefault(Return(true));
brillo::VariantDictionary props;
EXPECT_CALL(mock_manager_, GetProperties(_, _, _))
.WillOnce(DoAll(SetArgPointee<0>(props), Return(true)));
EXPECT_CALL(mock_manager_, SetDNSProxyIPv4Address(_, _, _))
.WillOnce(Return(true));
proxy.resolver = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::Device vpn;
vpn.type = shill::Client::Device::Type::kVPN;
vpn.state = shill::Client::Device::ConnectionState::kOnline;
shill_ptr->default_device_ = std::make_unique<shill::Client::Device>();
shill_ptr->default_device_->type = shill::Client::Device::Type::kWifi;
shill_ptr->default_device_->state =
shill::Client::Device::ConnectionState::kOnline;
proxy.OnDefaultDeviceChanged(&vpn);
EXPECT_TRUE(proxy.device_);
EXPECT_EQ(proxy.device_->type, shill::Client::Device::Type::kWifi);
}
TEST_F(ProxyTest, DefaultProxy_UsesVPN) {
TestProxy proxy(Proxy::Options{.type = Proxy::Type::kDefault},
PatchpanelClient(), ShillClient());
proxy.shill_ready_ = true;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
ON_CALL(*mock_resolver, ListenUDP(_)).WillByDefault(Return(true));
ON_CALL(*mock_resolver, ListenTCP(_)).WillByDefault(Return(true));
brillo::VariantDictionary props;
EXPECT_CALL(mock_manager_, GetProperties(_, _, _))
.WillOnce(DoAll(SetArgPointee<0>(props), Return(true)));
proxy.resolver = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::Device dev;
dev.type = shill::Client::Device::Type::kWifi;
dev.state = shill::Client::Device::ConnectionState::kOnline;
proxy.OnDefaultDeviceChanged(&dev);
EXPECT_TRUE(proxy.device_);
EXPECT_EQ(proxy.device_->type, shill::Client::Device::Type::kWifi);
dev.type = shill::Client::Device::Type::kVPN;
proxy.OnDefaultDeviceChanged(&dev);
EXPECT_TRUE(proxy.device_);
EXPECT_EQ(proxy.device_->type, shill::Client::Device::Type::kVPN);
}
TEST_F(ProxyTest, ArcProxy_NameServersUpdatedOnDeviceChangeEvent) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "wlan0"},
PatchpanelClient(), ShillClient());
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::Device dev;
dev.ifname = "wlan0";
dev.state = shill::Client::Device::ConnectionState::kOnline;
dev.ipconfig.ipv4_dns_addresses = {"8.8.8.8", "8.8.4.4"};
dev.ipconfig.ipv6_dns_addresses = {"2001:4860:4860::8888",
"2001:4860:4860::8844"};
// Doesn't call listen since the resolver already exists.
EXPECT_CALL(*mock_resolver, ListenUDP(_)).Times(0);
EXPECT_CALL(*mock_resolver, ListenTCP(_)).Times(0);
EXPECT_CALL(*mock_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);
// Verify it only applies changes for the correct interface.
dev.ifname = "eth0";
dev.ipconfig.ipv4_dns_addresses = {"8.8.8.8", "8.8.4.4", "1.1.1.1"};
EXPECT_CALL(*mock_resolver, SetNameServers(_)).Times(0);
proxy.OnDeviceChanged(&dev);
dev.ifname = "wlan0";
dev.ipconfig.ipv4_dns_addresses = {"8.8.8.8", "8.8.4.4", "1.1.1.1"};
dev.ipconfig.ipv6_dns_addresses.clear();
EXPECT_CALL(*mock_resolver,
SetNameServers(ElementsAre(StrEq("8.8.8.8"), StrEq("8.8.4.4"),
StrEq("1.1.1.1"))));
proxy.OnDeviceChanged(&dev);
}
TEST_F(ProxyTest, SystemProxy_NameServersUpdatedOnDeviceChangeEvent) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.shill_ready_ = true;
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::Device dev;
dev.state = shill::Client::Device::ConnectionState::kOnline;
dev.ipconfig.ipv4_dns_addresses = {"8.8.8.8", "8.8.4.4"};
dev.ipconfig.ipv6_dns_addresses = {"2001:4860:4860::8888",
"2001:4860:4860::8844"};
// Doesn't call listen since the resolver already exists.
EXPECT_CALL(mock_manager_, SetDNSProxyIPv4Address(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*mock_resolver, ListenUDP(_)).Times(0);
EXPECT_CALL(*mock_resolver, ListenTCP(_)).Times(0);
EXPECT_CALL(*mock_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);
// Now trigger an ipconfig change.
dev.ipconfig.ipv4_dns_addresses = {"8.8.8.8"};
EXPECT_CALL(*mock_resolver,
SetNameServers(ElementsAre(StrEq("8.8.8.8"),
StrEq("2001:4860:4860::8888"),
StrEq("2001:4860:4860::8844"))));
proxy.OnDeviceChanged(&dev);
}
TEST_F(ProxyTest, DeviceChangeEventIgnored) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.shill_ready_ = true;
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::Device dev;
dev.ifname = "eth0";
dev.state = shill::Client::Device::ConnectionState::kOnline;
dev.ipconfig.ipv4_dns_addresses = {"8.8.8.8", "8.8.4.4"};
dev.ipconfig.ipv6_dns_addresses = {"2001:4860:4860::8888",
"2001:4860:4860::8844"};
// Doesn't call listen since the resolver already exists.
EXPECT_CALL(mock_manager_, SetDNSProxyIPv4Address(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*mock_resolver, ListenUDP(_)).Times(0);
EXPECT_CALL(*mock_resolver, ListenTCP(_)).Times(0);
EXPECT_CALL(*mock_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);
// No change to ipconfig, no call to SetNameServers
proxy.OnDeviceChanged(&dev);
// Different ifname, no call to SetNameServers
dev.ifname = "wlan0";
proxy.OnDeviceChanged(&dev);
}
TEST_F(ProxyTest, BasicDoHDisable) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
EXPECT_CALL(*mock_resolver, SetDoHProviders(IsEmpty(), false));
brillo::VariantDictionary props;
proxy.OnDoHProvidersChanged(props);
}
TEST_F(ProxyTest, BasicDoHAlwaysOn) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
EXPECT_CALL(
*mock_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) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::IPConfig ipconfig;
ipconfig.ipv4_dns_addresses = {"8.8.4.4"};
proxy.UpdateNameServers(ipconfig);
EXPECT_CALL(
*mock_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, RemovesDNSQueryParameterTemplate_AlwaysOn) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
EXPECT_CALL(
*mock_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) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::IPConfig ipconfig;
ipconfig.ipv4_dns_addresses = {"8.8.4.4"};
proxy.UpdateNameServers(ipconfig);
EXPECT_CALL(
*mock_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, NewResolverConfiguredWhenSet) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = 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);
shill::Client::IPConfig ipconfig;
ipconfig.ipv4_dns_addresses = {"1.0.0.1", "1.1.1.1"};
proxy.UpdateNameServers(ipconfig);
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
EXPECT_CALL(*mock_resolver, SetNameServers(UnorderedElementsAre(
StrEq("1.1.1.1"), StrEq("1.0.0.1"))));
EXPECT_CALL(
*mock_resolver,
SetDoHProviders(
ElementsAre(StrEq("https://chrome.cloudflare-dns.com/dns-query")),
false));
proxy.doh_config_.set_resolver(mock_resolver);
}
TEST_F(ProxyTest, DoHModeChangingFixedNameServers) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
// Initially off.
EXPECT_CALL(*mock_resolver, SetDoHProviders(IsEmpty(), false));
shill::Client::IPConfig ipconfig;
ipconfig.ipv4_dns_addresses = {"1.1.1.1", "9.9.9.9"};
proxy.UpdateNameServers(ipconfig);
// Automatic mode - matched cloudflare.
EXPECT_CALL(
*mock_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(*mock_resolver, SetDoHProviders(IsEmpty(), false));
ipconfig.ipv4_dns_addresses = {"10.10.10.1"};
proxy.UpdateNameServers(ipconfig);
// Automatic mode - matched google.
EXPECT_CALL(
*mock_resolver,
SetDoHProviders(ElementsAre(StrEq("https://dns.google.com")), false));
ipconfig.ipv4_dns_addresses = {"8.8.4.4", "10.10.10.1", "8.8.8.8"};
proxy.UpdateNameServers(ipconfig);
// Explicitly turned off.
EXPECT_CALL(*mock_resolver, SetDoHProviders(IsEmpty(), false));
props.clear();
proxy.OnDoHProvidersChanged(props);
// Still off - even switching ns back.
EXPECT_CALL(*mock_resolver, SetDoHProviders(IsEmpty(), false));
ipconfig.ipv4_dns_addresses = {"8.8.4.4", "10.10.10.1", "8.8.8.8"};
proxy.UpdateNameServers(ipconfig);
// Always-on mode.
EXPECT_CALL(
*mock_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(*mock_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(
*mock_resolver,
SetDoHProviders(ElementsAre(StrEq("https://doh.opendns.com/dns-query")),
false));
ipconfig.ipv4_dns_addresses = {"8.8.8.8"};
ipconfig.ipv6_dns_addresses = {"2620:119:35::35"};
proxy.UpdateNameServers(ipconfig);
}
TEST_F(ProxyTest, MultipleDoHProvidersForAlwaysOnMode) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
EXPECT_CALL(
*mock_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) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::IPConfig ipconfig;
ipconfig.ipv4_dns_addresses = {"1.1.1.1", "10.10.10.10"};
proxy.UpdateNameServers(ipconfig);
EXPECT_CALL(
*mock_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(*mock_resolver,
SetDoHProviders(UnorderedElementsAre(
StrEq("https://dns.google.com"),
StrEq("https://doh.opendns.com/dns-query"),
StrEq("https://dns.quad9.net/dns-query")),
false));
ipconfig.ipv4_dns_addresses = {"8.8.8.8", "10.10.10.10"};
ipconfig.ipv6_dns_addresses = {"2620:fe::9", "2620:119:53::53"};
proxy.UpdateNameServers(ipconfig);
}
TEST_F(ProxyTest, DoHBadAlwaysOnConfigSetsAutomaticMode) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::IPConfig ipconfig;
ipconfig.ipv4_dns_addresses = {"1.1.1.1", "10.10.10.10"};
proxy.UpdateNameServers(ipconfig);
EXPECT_CALL(
*mock_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(*mock_resolver,
SetDoHProviders(UnorderedElementsAre(
StrEq("https://dns.google.com"),
StrEq("https://doh.opendns.com/dns-query"),
StrEq("https://dns.quad9.net/dns-query")),
false));
ipconfig.ipv4_dns_addresses = {"8.8.8.8", "10.10.10.10"};
ipconfig.ipv6_dns_addresses = {"2620:fe::9", "2620:119:53::53"};
proxy.UpdateNameServers(ipconfig);
}
TEST_F(ProxyTest, FeatureEnablementCheckedOnSetup) {
scoped_refptr<dbus::MockObjectProxy> mock = new dbus::MockObjectProxy(
mock_bus_.get(), shill::kFlimflamServiceName, dbus::ObjectPath("/"));
EXPECT_CALL(*mock_bus_, GetObjectProxy(_, _))
.WillRepeatedly(Return(mock.get()));
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.session_.reset(new FakeSessionMonitor(mock_bus_));
auto features = std::make_unique<FakeFeaturesClient>(true);
auto* features_ptr = features.get();
proxy.features_ = std::move(features);
ASSERT_FALSE(features_ptr->is_dnsproxy_enabled_called);
proxy.Setup();
EXPECT_TRUE(features_ptr->is_dnsproxy_enabled_called);
}
TEST_F(ProxyTest, LoginEventTriggersFeatureCheck) {
scoped_refptr<dbus::MockObjectProxy> mock = new dbus::MockObjectProxy(
mock_bus_.get(), shill::kFlimflamServiceName, dbus::ObjectPath("/"));
EXPECT_CALL(*mock_bus_, GetObjectProxy(_, _))
.WillRepeatedly(Return(mock.get()));
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
auto session = std::make_unique<FakeSessionMonitor>(mock_bus_);
auto* session_ptr = session.get();
proxy.session_ = std::move(session);
auto features = std::make_unique<FakeFeaturesClient>(true);
auto* features_ptr = features.get();
proxy.features_ = std::move(features);
proxy.Setup();
features_ptr->is_dnsproxy_enabled_called = false;
session_ptr->Login();
EXPECT_TRUE(features_ptr->is_dnsproxy_enabled_called);
}
TEST_F(ProxyTest, LogoutEventTriggersDisable) {
scoped_refptr<dbus::MockObjectProxy> mock = new dbus::MockObjectProxy(
mock_bus_.get(), shill::kFlimflamServiceName, dbus::ObjectPath("/"));
EXPECT_CALL(*mock_bus_, GetObjectProxy(_, _))
.WillRepeatedly(Return(mock.get()));
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
auto session = std::make_unique<FakeSessionMonitor>(mock_bus_);
auto* session_ptr = session.get();
proxy.session_ = std::move(session);
proxy.features_.reset(new FakeFeaturesClient(true));
proxy.Setup();
ASSERT_TRUE(proxy.feature_enabled_);
session_ptr->Logout();
EXPECT_FALSE(proxy.feature_enabled_);
}
TEST_F(ProxyTest, FeatureEnabled_LoginAfterLogout) {
scoped_refptr<dbus::MockObjectProxy> mock = new dbus::MockObjectProxy(
mock_bus_.get(), shill::kFlimflamServiceName, dbus::ObjectPath("/"));
EXPECT_CALL(*mock_bus_, GetObjectProxy(_, _))
.WillRepeatedly(Return(mock.get()));
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
auto session = std::make_unique<FakeSessionMonitor>(mock_bus_);
auto* session_ptr = session.get();
proxy.session_ = std::move(session);
proxy.features_.reset(new FakeFeaturesClient(true));
proxy.Setup();
session_ptr->Login();
EXPECT_TRUE(proxy.feature_enabled_);
session_ptr->Logout();
EXPECT_FALSE(proxy.feature_enabled_);
session_ptr->Login();
EXPECT_TRUE(proxy.feature_enabled_);
}
TEST_F(ProxyTest, FeatureDisabled_LoginAfterLogout) {
scoped_refptr<dbus::MockObjectProxy> mock = new dbus::MockObjectProxy(
mock_bus_.get(), shill::kFlimflamServiceName, dbus::ObjectPath("/"));
EXPECT_CALL(*mock_bus_, GetObjectProxy(_, _))
.WillRepeatedly(Return(mock.get()));
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
auto session = std::make_unique<FakeSessionMonitor>(mock_bus_);
auto* session_ptr = session.get();
proxy.session_ = std::move(session);
proxy.features_.reset(new FakeFeaturesClient(false));
proxy.Setup();
session_ptr->Login();
EXPECT_FALSE(proxy.feature_enabled_);
session_ptr->Logout();
EXPECT_FALSE(proxy.feature_enabled_);
session_ptr->Login();
EXPECT_FALSE(proxy.feature_enabled_);
}
TEST_F(ProxyTest, SystemProxy_ShillPropertyNotUpdatedIfFeatureDisabled) {
scoped_refptr<dbus::MockObjectProxy> mock = new dbus::MockObjectProxy(
mock_bus_.get(), shill::kFlimflamServiceName, dbus::ObjectPath("/"));
EXPECT_CALL(*mock_bus_, GetObjectProxy(_, _))
.WillRepeatedly(Return(mock.get()));
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
proxy.session_.reset(new FakeSessionMonitor(mock_bus_));
proxy.features_.reset(new FakeFeaturesClient(false));
proxy.Setup();
proxy.shill_ready_ = true;
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
shill::Client::Device dev;
dev.state = shill::Client::Device::ConnectionState::kOnline;
EXPECT_CALL(*mock_resolver, SetNameServers(_));
EXPECT_CALL(mock_manager_, SetDNSProxyIPv4Address(_, _, _)).Times(0);
proxy.OnDefaultDeviceChanged(&dev);
}
TEST_F(ProxyTest, DefaultProxy_DisableDoHProvidersOnVPN) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, PatchpanelClient(),
ShillClient());
proxy.device_ = std::make_unique<shill::Client::Device>();
proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
proxy.device_->type = shill::Client::Device::Type::kVPN;
auto resolver = std::make_unique<MockResolver>();
MockResolver* mock_resolver = resolver.get();
proxy.resolver_ = std::move(resolver);
proxy.doh_config_.set_resolver(mock_resolver);
EXPECT_CALL(*mock_resolver, SetDoHProviders(IsEmpty(), false));
brillo::VariantDictionary props;
props["https://dns.google.com"] = std::string("");
props["https://doh.opendns.com"] = std::string("");
proxy.OnDoHProvidersChanged(props);
}
TEST_F(ProxyTest, SystemProxy_NeverSetsDnsRedirectionRule) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, std::move(client),
ShillClient());
proxy.shill_ready_ = true;
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.ns_peer_ipv6_address_ = "::1";
// System proxy must not request a redirect DNS rule.
EXPECT_CALL(*mock_client, RedirectDns(_, _, _, _)).Times(0);
// Expect ConnectNamespace call and set the namespace address.
EXPECT_CALL(*mock_client, ConnectNamespace(_, _, _, _, _))
.WillRepeatedly([](pid_t pid, const std::string& outbound_ifname,
bool forward_user_traffic, bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) {
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
return std::make_pair(base::ScopedFD(make_fd()), resp);
});
// Set devices created before the proxy started.
patchpanel::NetworkDevice starting_device;
starting_device.set_ifname("vmtap0");
starting_device.set_phys_ifname("eth0");
starting_device.set_guest_type(patchpanel::NetworkDevice::TERMINA_VM);
proxy.OnPatchpanelReady(true);
EXPECT_CALL(mock_manager_, SetDNSProxyIPv4Address(_, _, _))
.WillOnce(Return(true));
proxy.Enable();
// Default device changed.
shill::Client::Device default_device;
default_device.ifname = "eth0";
default_device.state = shill::Client::Device::ConnectionState::kOnline;
default_device.ipconfig.ipv4_dns_addresses = {"8.8.8.8", "8.8.4.4"};
default_device.ipconfig.ipv6_dns_addresses = {"2001:4860:4860::8888",
"2001:4860:4860::8844"};
EXPECT_CALL(mock_manager_, SetDNSProxyIPv4Address(_, _, _))
.WillOnce(Return(true));
proxy.OnDefaultDeviceChanged(&default_device);
// Plugin VM started.
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* plugin_vm_dev = signal.mutable_device();
plugin_vm_dev->set_ifname("vmtap1");
plugin_vm_dev->set_phys_ifname("eth0");
plugin_vm_dev->set_guest_type(patchpanel::NetworkDevice::PLUGIN_VM);
proxy.OnVirtualDeviceChanged(signal);
// ARC started.
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* arc_dev = signal.mutable_device();
arc_dev->set_ifname("arc_eth0");
arc_dev->set_phys_ifname("eth0");
arc_dev->set_guest_type(patchpanel::NetworkDevice::ARC);
proxy.OnVirtualDeviceChanged(signal);
}
TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleDeviceAlreadyStarted) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(client),
ShillClient());
proxy.shill_ready_ = true;
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.ns_peer_ipv6_address_ = "::1";
// Expect ConnectNamespace call and set the namespace address.
EXPECT_CALL(*mock_client, ConnectNamespace(_, _, _, _, _))
.WillRepeatedly([](pid_t pid, const std::string& outbound_ifname,
bool forward_user_traffic, bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) {
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
return std::make_pair(base::ScopedFD(make_fd()), resp);
});
// Set devices created before the proxy started.
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("vmtap0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::TERMINA_VM);
EXPECT_CALL(*mock_client, GetDevices())
.WillOnce(Return(std::vector<patchpanel::NetworkDevice>{*dev}));
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::DEFAULT,
"vmtap0", "10.10.10.10", IsEmpty()))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::DEFAULT,
"vmtap0", "::1", IsEmpty()))
.Times(0);
proxy.OnPatchpanelReady(true);
proxy.Enable();
EXPECT_EQ(proxy.lifeline_fds_.size(), 1);
// Default device changed.
shill::Client::Device default_device;
default_device.state = shill::Client::Device::ConnectionState::kOnline;
std::vector<std::string> ipv4_dns_addresses = {"8.8.8.8", "8.8.4.4"};
default_device.ipconfig.ipv4_dns_addresses = ipv4_dns_addresses;
std::vector<std::string> ipv6_dns_addresses = {"2001:4860:4860::8888",
"2001:4860:4860::8844"};
default_device.ipconfig.ipv6_dns_addresses = ipv6_dns_addresses;
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::USER, _, _,
ipv4_dns_addresses))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::USER, _, _,
ipv6_dns_addresses))
.Times(0);
proxy.OnDefaultDeviceChanged(&default_device);
EXPECT_EQ(proxy.lifeline_fds_.size(), 2);
// Guest stopped.
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_REMOVED);
proxy.OnVirtualDeviceChanged(signal);
EXPECT_EQ(proxy.lifeline_fds_.size(), 1);
}
TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleNewDeviceStarted) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(client),
ShillClient());
proxy.shill_ready_ = true;
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.ns_peer_ipv6_address_ = "::1";
// Expect ConnectNamespace call and set the namespace address.
EXPECT_CALL(*mock_client, ConnectNamespace(_, _, _, _, _))
.WillRepeatedly([](pid_t pid, const std::string& outbound_ifname,
bool forward_user_traffic, bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) {
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
return std::make_pair(base::ScopedFD(make_fd()), resp);
});
EXPECT_CALL(*mock_client, RedirectDns(_, _, _, _)).Times(0);
proxy.OnPatchpanelReady(true);
proxy.Enable();
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
// Default device changed.
shill::Client::Device default_device;
default_device.state = shill::Client::Device::ConnectionState::kOnline;
std::vector<std::string> ipv4_dns_addresses = {"8.8.8.8", "8.8.4.4"};
default_device.ipconfig.ipv4_dns_addresses = ipv4_dns_addresses;
std::vector<std::string> ipv6_dns_addresses = {"2001:4860:4860::8888",
"2001:4860:4860::8844"};
default_device.ipconfig.ipv6_dns_addresses = ipv6_dns_addresses;
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::USER, _, _,
ipv4_dns_addresses))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::USER, _, _,
ipv6_dns_addresses))
.Times(0);
proxy.OnDefaultDeviceChanged(&default_device);
EXPECT_EQ(proxy.lifeline_fds_.size(), 1);
// Guest started.
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("vmtap0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::PLUGIN_VM);
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::DEFAULT,
"vmtap0", "10.10.10.10", IsEmpty()))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::DEFAULT,
"vmtap0", "::1", IsEmpty()))
.Times(0);
proxy.OnVirtualDeviceChanged(signal);
EXPECT_EQ(proxy.lifeline_fds_.size(), 2);
// Guest stopped.
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_REMOVED);
proxy.OnVirtualDeviceChanged(signal);
EXPECT_EQ(proxy.lifeline_fds_.size(), 1);
}
TEST_F(ProxyTest, DefaultProxy_NeverSetsDnsRedirectionRuleOtherGuest) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(client),
ShillClient());
proxy.shill_ready_ = true;
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.ns_peer_ipv6_address_ = "::1";
EXPECT_CALL(*mock_client, RedirectDns(_, _, _, _)).Times(0);
// Expect ConnectNamespace call and set the namespace address.
EXPECT_CALL(*mock_client, ConnectNamespace(_, _, _, _, _))
.WillRepeatedly([](pid_t pid, const std::string& outbound_ifname,
bool forward_user_traffic, bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) {
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
return std::make_pair(base::ScopedFD(make_fd()), resp);
});
// Set devices created before the proxy started.
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("arc_eth0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::ARCVM);
EXPECT_CALL(*mock_client, GetDevices())
.WillOnce(Return(std::vector<patchpanel::NetworkDevice>{*dev}));
proxy.OnPatchpanelReady(true);
proxy.Enable();
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
proxy.OnVirtualDeviceChanged(signal);
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
}
TEST_F(ProxyTest, DefaultProxy_NeverSetsDnsRedirectionRuleFeatureDisabled) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(client),
ShillClient());
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.shill_ready_ = true;
proxy.ns_peer_ipv6_address_ = "::1";
proxy.feature_enabled_ = false;
EXPECT_CALL(*mock_client, RedirectDns(_, _, _, _)).Times(0);
// Expect ConnectNamespace call and set the namespace address.
EXPECT_CALL(*mock_client, ConnectNamespace(_, _, _, _, _))
.WillRepeatedly([](pid_t pid, const std::string& outbound_ifname,
bool forward_user_traffic, bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) {
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
return std::make_pair(base::ScopedFD(make_fd()), resp);
});
// Set devices created before the proxy started.
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("vmtap0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::TERMINA_VM);
proxy.OnPatchpanelReady(true);
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
proxy.OnVirtualDeviceChanged(signal);
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
}
TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleWithoutIPv6) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(client),
ShillClient());
proxy.resolver_ = std::make_unique<MockResolver>();
// Expect ConnectNamespace call and set the namespace address.
EXPECT_CALL(*mock_client, ConnectNamespace(_, _, _, _, _))
.WillRepeatedly([](pid_t pid, const std::string& outbound_ifname,
bool forward_user_traffic, bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) {
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
return std::make_pair(base::ScopedFD(make_fd()), resp);
});
EXPECT_CALL(*mock_client, RedirectDns(_, _, _, _)).Times(0);
proxy.OnPatchpanelReady(true);
proxy.Enable();
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
// Default device changed.
shill::Client::Device default_device;
default_device.state = shill::Client::Device::ConnectionState::kOnline;
std::vector<std::string> ipv4_dns_addresses = {"8.8.8.8", "8.8.4.4"};
default_device.ipconfig.ipv4_dns_addresses = ipv4_dns_addresses;
default_device.ipconfig.ipv6_dns_addresses = {"2001:4860:4860::8888",
"2001:4860:4860::8844"};
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::USER, _, _,
ipv4_dns_addresses))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
proxy.OnDefaultDeviceChanged(&default_device);
EXPECT_EQ(proxy.lifeline_fds_.size(), 1);
// Guest started.
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("vmtap0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::PLUGIN_VM);
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::DEFAULT,
"vmtap0", "10.10.10.10", IsEmpty()))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
proxy.OnVirtualDeviceChanged(signal);
EXPECT_EQ(proxy.lifeline_fds_.size(), 2);
// Guest stopped.
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_REMOVED);
proxy.OnVirtualDeviceChanged(signal);
EXPECT_EQ(proxy.lifeline_fds_.size(), 1);
}
TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleIPv6Added) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(client),
ShillClient());
proxy.ns_.set_peer_ifname("");
proxy.Enable();
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("vmtap0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::TERMINA_VM);
std::string peer_ipv6_addr = "::1";
struct in6_addr ipv6_addr;
inet_pton(AF_INET6, peer_ipv6_addr.c_str(), &ipv6_addr.s6_addr);
std::vector<std::string> ipv6_dns_addresses = {"2001:4860:4860::8888",
"2001:4860:4860::8844"};
proxy.doh_config_.set_nameservers({"8.8.8.8", "8.8.4.4"}, ipv6_dns_addresses);
proxy.device_.reset(new shill::Client::Device{});
EXPECT_CALL(*mock_client, GetDevices())
.WillOnce(Return(std::vector<patchpanel::NetworkDevice>{*dev}));
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::USER, _, _,
ipv6_dns_addresses))
.Times(0);
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::DEFAULT,
"vmtap0", peer_ipv6_addr, IsEmpty()))
.Times(0);
// 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.
shill::RTNLMessage msg(shill::RTNLMessage::kTypeAddress,
shill::RTNLMessage::kModeAdd, 0 /* flags */,
0 /* seq */, 0 /* pid */, 0 /* interface_index */,
shill::IPAddress::Family(AF_INET6));
msg.set_address_status(
shill::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE));
msg.SetAttribute(IFA_ADDRESS,
shill::ByteString(ipv6_addr.s6_addr, sizeof(ipv6_addr)));
proxy.RTNLMessageHandler(msg);
}
TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleIPv6Deleted) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(client),
ShillClient());
proxy.ns_.set_peer_ifname("");
proxy.device_.reset(new shill::Client::Device{});
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()));
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("vmtap0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::TERMINA_VM);
EXPECT_CALL(*mock_client, GetDevices())
.WillOnce(Return(std::vector<patchpanel::NetworkDevice>{*dev}));
shill::RTNLMessage msg(shill::RTNLMessage::kTypeAddress,
shill::RTNLMessage::kModeDelete, 0 /* flags */,
0 /* seq */, 0 /* pid */, 0 /* interface_index */,
shill::IPAddress::Family(AF_INET6));
msg.set_address_status(
shill::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE));
proxy.RTNLMessageHandler(msg);
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
}
TEST_F(ProxyTest, DefaultProxy_SetDnsRedirectionRuleUnrelatedIPv6Added) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(client),
ShillClient());
proxy.ns_.set_peer_ifname("");
proxy.Enable();
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("vmtap0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::TERMINA_VM);
std::string peer_ipv6_addr = "::1";
struct in6_addr ipv6_addr;
inet_pton(AF_INET6, peer_ipv6_addr.c_str(), &ipv6_addr.s6_addr);
std::vector<std::string> ipv6_dns_addresses = {"2001:4860:4860::8888",
"2001:4860:4860::8844"};
proxy.doh_config_.set_nameservers({"8.8.8.8", "8.8.4.4"}, ipv6_dns_addresses);
proxy.device_.reset(new shill::Client::Device{});
EXPECT_CALL(*mock_client, GetDevices())
.WillRepeatedly(Return(std::vector<patchpanel::NetworkDevice>{*dev}));
EXPECT_CALL(*mock_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.
shill::RTNLMessage msg_unrelated_ifindex(
shill::RTNLMessage::kTypeAddress, shill::RTNLMessage::kModeAdd,
0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */,
shill::IPAddress::Family(AF_INET6));
msg_unrelated_ifindex.set_address_status(
shill::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE));
msg_unrelated_ifindex.SetAttribute(
IFA_ADDRESS, shill::ByteString(ipv6_addr.s6_addr, sizeof(ipv6_addr)));
proxy.RTNLMessageHandler(msg_unrelated_ifindex);
shill::RTNLMessage msg_unrelated_scope(
shill::RTNLMessage::kTypeAddress, shill::RTNLMessage::kModeAdd,
0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */,
shill::IPAddress::Family(AF_INET6));
msg_unrelated_scope.set_address_status(
shill::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_LINK));
msg_unrelated_scope.SetAttribute(
IFA_ADDRESS, shill::ByteString(ipv6_addr.s6_addr, sizeof(ipv6_addr)));
proxy.RTNLMessageHandler(msg_unrelated_scope);
}
TEST_F(ProxyTest, ArcProxy_SetDnsRedirectionRuleDeviceAlreadyStarted) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
std::move(client), ShillClient());
proxy.shill_ready_ = true;
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.ns_peer_ipv6_address_ = "::1";
// Expect ConnectNamespace call and set the namespace address.
EXPECT_CALL(*mock_client, ConnectNamespace(_, _, _, _, _))
.WillRepeatedly([](pid_t pid, const std::string& outbound_ifname,
bool forward_user_traffic, bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) {
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
return std::make_pair(base::ScopedFD(make_fd()), resp);
});
// Set devices created before the proxy started.
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("arc_eth0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::ARCVM);
EXPECT_CALL(*mock_client, GetDevices())
.WillOnce(Return(std::vector<patchpanel::NetworkDevice>{*dev}));
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::ARC,
"arc_eth0", "10.10.10.10", IsEmpty()))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::ARC,
"arc_eth0", "::1", IsEmpty()))
.Times(0);
proxy.OnPatchpanelReady(true);
proxy.Enable();
EXPECT_EQ(proxy.lifeline_fds_.size(), 1);
}
TEST_F(ProxyTest, ArcProxy_SetDnsRedirectionRuleNewDeviceStarted) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
std::move(client), ShillClient());
proxy.shill_ready_ = true;
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.ns_peer_ipv6_address_ = "::1";
// Expect ConnectNamespace call and set the namespace address.
EXPECT_CALL(*mock_client, ConnectNamespace(_, _, _, _, _))
.WillRepeatedly([](pid_t pid, const std::string& outbound_ifname,
bool forward_user_traffic, bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) {
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
return std::make_pair(base::ScopedFD(make_fd()), resp);
});
EXPECT_CALL(*mock_client, RedirectDns(_, _, _, _)).Times(0);
proxy.OnPatchpanelReady(true);
proxy.Enable();
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
// Guest started.
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("arc_eth0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::ARC);
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::ARC,
"arc_eth0", "10.10.10.10", IsEmpty()))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::ARC,
"arc_eth0", "::1", IsEmpty()))
.Times(0);
proxy.OnVirtualDeviceChanged(signal);
EXPECT_EQ(proxy.lifeline_fds_.size(), 1);
}
TEST_F(ProxyTest, ArcProxy_NeverSetsDnsRedirectionRuleOtherGuest) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
std::move(client), ShillClient());
proxy.shill_ready_ = true;
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.ns_peer_ipv6_address_ = "::1";
EXPECT_CALL(*mock_client, RedirectDns(_, _, _, _)).Times(0);
// Expect ConnectNamespace call and set the namespace address.
EXPECT_CALL(*mock_client, ConnectNamespace(_, _, _, _, _))
.WillRepeatedly([](pid_t pid, const std::string& outbound_ifname,
bool forward_user_traffic, bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) {
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
return std::make_pair(base::ScopedFD(make_fd()), resp);
});
// Set devices created before the proxy started.
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("vmtap0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::TERMINA_VM);
EXPECT_CALL(*mock_client, GetDevices())
.WillOnce(Return(std::vector<patchpanel::NetworkDevice>{*dev}));
proxy.OnPatchpanelReady(true);
proxy.Enable();
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
proxy.OnVirtualDeviceChanged(signal);
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
}
TEST_F(ProxyTest, ArcProxy_NeverSetsDnsRedirectionRuleOtherIfname) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "wlan0"},
std::move(client), ShillClient());
proxy.shill_ready_ = true;
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.ns_peer_ipv6_address_ = "::1";
EXPECT_CALL(*mock_client, RedirectDns(_, _, _, _)).Times(0);
// Expect ConnectNamespace call and set the namespace address.
EXPECT_CALL(*mock_client, ConnectNamespace(_, _, _, _, _))
.WillRepeatedly([](pid_t pid, const std::string& outbound_ifname,
bool forward_user_traffic, bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) {
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
return std::make_pair(base::ScopedFD(make_fd()), resp);
});
// Set devices created before the proxy started.
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("arc_eth0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::ARCVM);
EXPECT_CALL(*mock_client, GetDevices())
.WillOnce(Return(std::vector<patchpanel::NetworkDevice>{*dev}));
proxy.OnPatchpanelReady(true);
proxy.Enable();
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
proxy.OnVirtualDeviceChanged(signal);
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
}
TEST_F(ProxyTest, ArcProxy_NeverSetsDnsRedirectionRuleFeatureDisabled) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
std::move(client), ShillClient());
proxy.resolver_ = std::make_unique<MockResolver>();
proxy.shill_ready_ = true;
proxy.ns_peer_ipv6_address_ = "::1";
proxy.feature_enabled_ = false;
EXPECT_CALL(*mock_client, RedirectDns(_, _, _, _)).Times(0);
// Expect ConnectNamespace call and set the namespace address.
EXPECT_CALL(*mock_client, ConnectNamespace(_, _, _, _, _))
.WillRepeatedly([](pid_t pid, const std::string& outbound_ifname,
bool forward_user_traffic, bool route_on_vpn,
patchpanel::TrafficCounter::Source traffic_source) {
patchpanel::ConnectNamespaceResponse resp;
resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
return std::make_pair(base::ScopedFD(make_fd()), resp);
});
// Set devices created before the proxy started.
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("arc_eth0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::ARCVM);
proxy.OnPatchpanelReady(true);
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
proxy.OnVirtualDeviceChanged(signal);
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
}
TEST_F(ProxyTest, ArcProxy_SetDnsRedirectionRuleIPv6Added) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
std::move(client), ShillClient());
proxy.ns_.set_peer_ifname("");
proxy.Enable();
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("arc_eth0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::ARCVM);
std::string peer_ipv6_addr = "::1";
struct in6_addr ipv6_addr;
inet_pton(AF_INET6, peer_ipv6_addr.c_str(), &ipv6_addr.s6_addr);
EXPECT_CALL(*mock_client, GetDevices())
.WillOnce(Return(std::vector<patchpanel::NetworkDevice>{*dev}));
EXPECT_CALL(*mock_client,
RedirectDns(patchpanel::SetDnsRedirectionRuleRequest::ARC,
"arc_eth0", peer_ipv6_addr, IsEmpty()))
.Times(0);
// 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.
shill::RTNLMessage msg(shill::RTNLMessage::kTypeAddress,
shill::RTNLMessage::kModeAdd, 0 /* flags */,
0 /* seq */, 0 /* pid */, 0 /* interface_index */,
shill::IPAddress::Family(AF_INET6));
msg.set_address_status(
shill::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE));
msg.SetAttribute(IFA_ADDRESS,
shill::ByteString(ipv6_addr.s6_addr, sizeof(ipv6_addr)));
proxy.RTNLMessageHandler(msg);
}
TEST_F(ProxyTest, ArcProxy_SetDnsRedirectionRuleIPv6Deleted) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
std::move(client), ShillClient());
proxy.ns_.set_peer_ifname("");
proxy.device_.reset(new shill::Client::Device{});
proxy.lifeline_fds_.emplace(std::make_pair("arc_eth0", AF_INET6),
base::ScopedFD(make_fd()));
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("arc_eth0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::ARCVM);
EXPECT_CALL(*mock_client, GetDevices())
.WillOnce(Return(std::vector<patchpanel::NetworkDevice>{*dev}));
shill::RTNLMessage msg(shill::RTNLMessage::kTypeAddress,
shill::RTNLMessage::kModeDelete, 0 /* flags */,
0 /* seq */, 0 /* pid */, 0 /* interface_index */,
shill::IPAddress::Family(AF_INET6));
msg.set_address_status(
shill::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE));
proxy.RTNLMessageHandler(msg);
EXPECT_EQ(proxy.lifeline_fds_.size(), 0);
}
TEST_F(ProxyTest, ArcProxy_SetDnsRedirectionRuleUnrelatedIPv6Added) {
auto client = std::make_unique<MockPatchpanelClient>();
MockPatchpanelClient* mock_client = client.get();
Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
std::move(client), ShillClient());
proxy.ns_.set_peer_ifname("");
proxy.Enable();
patchpanel::NetworkDeviceChangedSignal signal;
signal.set_event(patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED);
auto* dev = signal.mutable_device();
dev->set_ifname("arc_eth0");
dev->set_phys_ifname("eth0");
dev->set_guest_type(patchpanel::NetworkDevice::ARCVM);
std::string peer_ipv6_addr = "::1";
struct in6_addr ipv6_addr;
inet_pton(AF_INET6, peer_ipv6_addr.c_str(), &ipv6_addr.s6_addr);
EXPECT_CALL(*mock_client, GetDevices())
.WillRepeatedly(Return(std::vector<patchpanel::NetworkDevice>{*dev}));
EXPECT_CALL(*mock_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.
shill::RTNLMessage msg_unrelated_ifindex(
shill::RTNLMessage::kTypeAddress, shill::RTNLMessage::kModeAdd,
0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */,
shill::IPAddress::Family(AF_INET6));
msg_unrelated_ifindex.set_address_status(
shill::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE));
msg_unrelated_ifindex.SetAttribute(
IFA_ADDRESS, shill::ByteString(ipv6_addr.s6_addr, sizeof(ipv6_addr)));
proxy.RTNLMessageHandler(msg_unrelated_ifindex);
shill::RTNLMessage msg_unrelated_scope(
shill::RTNLMessage::kTypeAddress, shill::RTNLMessage::kModeAdd,
0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */,
shill::IPAddress::Family(AF_INET6));
msg_unrelated_scope.set_address_status(
shill::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_LINK));
msg_unrelated_scope.SetAttribute(
IFA_ADDRESS, shill::ByteString(ipv6_addr.s6_addr, sizeof(ipv6_addr)));
proxy.RTNLMessageHandler(msg_unrelated_scope);
}
TEST_F(ProxyTest, UpdateNameServers) {
Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
ShillClient());
shill::Client::IPConfig ipconfig;
ipconfig.ipv4_dns_addresses = {"8.8.4.4",
"192.168.1.1",
"256.256.256.256",
"0.0.0.0",
"eeb0:117e:92ee:ad3d:ce0d:a646:95ea:a16e",
"::1",
"::",
"a",
""};
ipconfig.ipv6_dns_addresses = {"8.8.4.4",
"192.168.1.1",
"256.256.256.256",
"0.0.0.0",
"eeb0:117e:92ee:ad3d:ce0d:a646:95ea:a16e",
"::1",
"::",
"a",
""};
proxy.UpdateNameServers(ipconfig);
const std::string expected_ipv4_dns_addresses[] = {"8.8.4.4", "192.168.1.1"};
const std::string expected_ipv6_dns_addresses[] = {
"eeb0:117e:92ee:ad3d:ce0d:a646:95ea:a16e", "::1"};
EXPECT_THAT(proxy.doh_config_.ipv4_nameservers(),
ElementsAreArray(expected_ipv4_dns_addresses));
EXPECT_THAT(proxy.doh_config_.ipv6_nameservers(),
ElementsAreArray(expected_ipv6_dns_addresses));
}
} // namespace dns_proxy