blob: 031d36f7f48c7e296689de4c86e6d2f2ca83ea9a [file] [log] [blame]
Garrick Evans5fe2a4f2021-02-03 17:04:48 +09001// Copyright 2021 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "dns-proxy/proxy.h"
6
7#include <fcntl.h>
8#include <sys/stat.h>
9
10#include <memory>
11#include <utility>
Garrick Evans2ca050d2021-02-09 18:21:36 +090012#include <vector>
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090013
14#include <chromeos/patchpanel/net_util.h>
15#include <chromeos/patchpanel/dbus/fake_client.h>
16#include <dbus/mock_bus.h>
17#include <gmock/gmock.h>
18#include <gtest/gtest.h>
19#include <shill/dbus/client/fake_client.h>
20#include <shill/dbus-constants.h>
21#include <shill/dbus-proxy-mocks.h>
22
23namespace dns_proxy {
Jason Jeremy Iman1bb71c22021-01-26 21:49:55 +090024namespace {
25constexpr base::TimeDelta kRequestTimeout = base::TimeDelta::FromSeconds(10000);
26} // namespace
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090027using org::chromium::flimflam::ManagerProxyInterface;
28using org::chromium::flimflam::ManagerProxyMock;
29using testing::_;
30using testing::Return;
Garrick Evans2ca050d2021-02-09 18:21:36 +090031using testing::StrEq;
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090032
33class FakeShillClient : public shill::FakeClient {
34 public:
35 FakeShillClient(scoped_refptr<dbus::Bus> bus,
36 ManagerProxyInterface* manager_proxy)
37 : shill::FakeClient(bus), manager_proxy_(manager_proxy) {}
38
39 std::unique_ptr<shill::Client::ManagerPropertyAccessor> ManagerProperties(
40 const base::TimeDelta& timeout) const override {
41 return std::make_unique<shill::Client::ManagerPropertyAccessor>(
42 manager_proxy_);
43 }
44
45 bool IsInitialized() const { return init_; }
46
47 private:
48 ManagerProxyInterface* manager_proxy_;
49};
50
51class FakePatchpanelClient : public patchpanel::FakeClient {
52 public:
53 FakePatchpanelClient() = default;
54 ~FakePatchpanelClient() = default;
55
56 void SetConnectNamespaceResult(
57 int fd, const patchpanel::ConnectNamespaceResponse& resp) {
58 ns_fd_ = fd;
59 ns_resp_ = resp;
60 }
61
62 std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse>
63 ConnectNamespace(pid_t pid,
64 const std::string& outbound_ifname,
65 bool forward_user_traffic,
66 bool route_on_vpn,
67 patchpanel::TrafficCounter::Source traffic_source) override {
68 ns_ifname_ = outbound_ifname;
69 ns_rvpn_ = route_on_vpn;
70 ns_ts_ = traffic_source;
71 return {base::ScopedFD(ns_fd_), ns_resp_};
72 }
73
74 std::string ns_ifname_;
75 bool ns_rvpn_;
76 patchpanel::TrafficCounter::Source ns_ts_;
77 int ns_fd_;
78 patchpanel::ConnectNamespaceResponse ns_resp_;
79};
80
Garrick Evans2ca050d2021-02-09 18:21:36 +090081class MockResolver : public Resolver {
82 public:
Jason Jeremy Iman1bb71c22021-01-26 21:49:55 +090083 MockResolver() : Resolver(kRequestTimeout) {}
Garrick Evans2ca050d2021-02-09 18:21:36 +090084 ~MockResolver() = default;
85
86 MOCK_METHOD(bool, Listen, (struct sockaddr*), (override));
87 MOCK_METHOD(void,
88 SetNameServers,
89 (const std::vector<std::string>&),
90 (override));
91 MOCK_METHOD(void,
92 SetDoHProviders,
93 (const std::vector<std::string>&, bool),
94 (override));
95};
96
97class TestProxy : public Proxy {
98 public:
99 TestProxy(const Options& opts,
100 std::unique_ptr<patchpanel::Client> patchpanel,
101 std::unique_ptr<shill::Client> shill)
102 : Proxy(opts, std::move(patchpanel), std::move(shill)) {}
103
104 std::unique_ptr<Resolver> resolver;
Jason Jeremy Iman1bb71c22021-01-26 21:49:55 +0900105 std::unique_ptr<Resolver> NewResolver(
106 base::TimeDelta request_timeout) override {
Garrick Evans2ca050d2021-02-09 18:21:36 +0900107 return std::move(resolver);
108 }
109};
110
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900111class ProxyTest : public ::testing::Test {
112 protected:
113 ProxyTest() : mock_bus_(new dbus::MockBus{dbus::Bus::Options{}}) {}
114 ~ProxyTest() { mock_bus_->ShutdownAndBlock(); }
115
116 std::unique_ptr<FakePatchpanelClient> PatchpanelClient() const {
117 return std::make_unique<FakePatchpanelClient>();
118 }
119
120 std::unique_ptr<FakeShillClient> ShillClient() const {
121 return std::make_unique<FakeShillClient>(
122 mock_bus_, reinterpret_cast<ManagerProxyInterface*>(
123 const_cast<ManagerProxyMock*>(&mock_manager_)));
124 }
125
126 int make_fd() const {
127 std::string fn(
128 ::testing::UnitTest::GetInstance()->current_test_info()->name());
129 fn = "/tmp/" + fn;
130 return open(fn.c_str(), O_CREAT, 0600);
131 }
132
133 protected:
134 scoped_refptr<dbus::MockBus> mock_bus_;
135 ManagerProxyMock mock_manager_;
136};
137
138TEST_F(ProxyTest, SystemProxy_OnShutdownClearsAddressPropertyOnShill) {
139 EXPECT_CALL(mock_manager_, SetProperty(shill::kDNSProxyIPv4AddressProperty,
140 brillo::Any(std::string()), _, _))
141 .WillOnce(Return(true));
142 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
143 ShillClient());
144 int unused;
145 proxy.OnShutdown(&unused);
146}
147
148TEST_F(ProxyTest, NonSystemProxy_OnShutdownDoesNotCallShill) {
149 EXPECT_CALL(mock_manager_, SetProperty(_, _, _, _)).Times(0);
150 Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, PatchpanelClient(),
151 ShillClient());
152 int unused;
153 proxy.OnShutdown(&unused);
154}
155
156TEST_F(ProxyTest, SystemProxy_SetShillPropertyWithNoRetriesCrashes) {
157 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
158 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
159 ShillClient());
160 EXPECT_DEATH(proxy.SetShillProperty("10.10.10.10", true, 0), "");
161}
162
163TEST_F(ProxyTest, SystemProxy_SetShillPropertyDoesntCrashIfDieFalse) {
164 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
165 EXPECT_CALL(mock_manager_, SetProperty(_, _, _, _)).Times(0);
166 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
167 ShillClient());
168 proxy.SetShillProperty("10.10.10.10", false, 0);
169}
170
171TEST_F(ProxyTest, SetupInitializesShill) {
172 auto shill = ShillClient();
173 auto* shill_ptr = shill.get();
174 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
175 std::move(shill));
176 proxy.Setup();
177 EXPECT_TRUE(shill_ptr->IsInitialized());
178}
179
180TEST_F(ProxyTest, SystemProxy_ConnectedNamedspace) {
181 auto pp = PatchpanelClient();
182 auto* pp_ptr = pp.get();
183 pp->SetConnectNamespaceResult(make_fd(),
184 patchpanel::ConnectNamespaceResponse());
185 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, std::move(pp),
186 ShillClient());
187 proxy.OnPatchpanelReady(true);
188 EXPECT_TRUE(pp_ptr->ns_ifname_.empty());
189 EXPECT_FALSE(pp_ptr->ns_rvpn_);
190 EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::SYSTEM);
191}
192
193TEST_F(ProxyTest, DefaultProxy_ConnectedNamedspace) {
194 auto pp = PatchpanelClient();
195 auto* pp_ptr = pp.get();
196 pp->SetConnectNamespaceResult(make_fd(),
197 patchpanel::ConnectNamespaceResponse());
198 Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(pp),
199 ShillClient());
200 proxy.OnPatchpanelReady(true);
201 EXPECT_TRUE(pp_ptr->ns_ifname_.empty());
202 EXPECT_TRUE(pp_ptr->ns_rvpn_);
203 EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::USER);
204}
205
206TEST_F(ProxyTest, ArcProxy_ConnectedNamedspace) {
207 auto pp = PatchpanelClient();
208 auto* pp_ptr = pp.get();
209 pp->SetConnectNamespaceResult(make_fd(),
210 patchpanel::ConnectNamespaceResponse());
211 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
212 std::move(pp), ShillClient());
213 proxy.OnPatchpanelReady(true);
214 EXPECT_EQ(pp_ptr->ns_ifname_, "eth0");
215 EXPECT_FALSE(pp_ptr->ns_rvpn_);
216 EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::ARC);
217}
218
219TEST_F(ProxyTest, CrashOnConnectNamespaceFailure) {
Garrick Evans2ca050d2021-02-09 18:21:36 +0900220 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900221 auto pp = PatchpanelClient();
222 pp->SetConnectNamespaceResult(-1 /* invalid fd */,
223 patchpanel::ConnectNamespaceResponse());
224 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
225 std::move(pp), ShillClient());
226 EXPECT_DEATH(proxy.OnPatchpanelReady(true), "namespace");
227}
228
229TEST_F(ProxyTest, CrashOnPatchpanelNotReady) {
Garrick Evans2ca050d2021-02-09 18:21:36 +0900230 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900231 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
232 PatchpanelClient(), ShillClient());
233 EXPECT_DEATH(proxy.OnPatchpanelReady(false), "patchpanel");
234}
235
236TEST_F(ProxyTest, ShillResetRestoresAddressProperty) {
237 auto pp = PatchpanelClient();
238 patchpanel::ConnectNamespaceResponse resp;
239 resp.set_host_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
240 pp->SetConnectNamespaceResult(make_fd(), resp);
241 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, std::move(pp),
242 ShillClient());
243 proxy.OnPatchpanelReady(true);
244 EXPECT_CALL(mock_manager_,
245 SetProperty(shill::kDNSProxyIPv4AddressProperty,
246 brillo::Any(std::string("10.10.10.10")), _, _))
247 .WillOnce(Return(true));
248 proxy.OnShillReset(true);
249}
250
Garrick Evans2ca050d2021-02-09 18:21:36 +0900251TEST_F(ProxyTest, StateClearedIfDefaultServiceDrops) {
252 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
253 ShillClient());
254 proxy.device_ = std::make_unique<shill::Client::Device>();
255 proxy.resolver_ = std::make_unique<MockResolver>();
256 proxy.OnDefaultDeviceChanged(nullptr /* no service */);
257 EXPECT_FALSE(proxy.device_);
258 EXPECT_FALSE(proxy.resolver_);
259}
260
261TEST_F(ProxyTest, ArcProxy_IgnoredIfDefaultServiceDrops) {
262 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC}, PatchpanelClient(),
263 ShillClient());
264 proxy.device_ = std::make_unique<shill::Client::Device>();
265 proxy.resolver_ = std::make_unique<MockResolver>();
266 proxy.OnDefaultDeviceChanged(nullptr /* no service */);
267 EXPECT_TRUE(proxy.device_);
268 EXPECT_TRUE(proxy.resolver_);
269}
270
271TEST_F(ProxyTest, StateClearedIfDefaultServiceIsNotOnline) {
272 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
273 ShillClient());
274 proxy.device_ = std::make_unique<shill::Client::Device>();
275 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
276 proxy.resolver_ = std::make_unique<MockResolver>();
277 shill::Client::Device dev;
278 dev.state = shill::Client::Device::ConnectionState::kReady;
279 proxy.OnDefaultDeviceChanged(&dev);
280 EXPECT_FALSE(proxy.device_);
281 EXPECT_FALSE(proxy.resolver_);
282}
283
284TEST_F(ProxyTest, NewResolverStartsListeningOnDefaultServiceComesOnline) {
285 TestProxy proxy(Proxy::Options{.type = Proxy::Type::kDefault},
286 PatchpanelClient(), ShillClient());
287 proxy.device_ = std::make_unique<shill::Client::Device>();
288 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
289 auto resolver = std::make_unique<MockResolver>();
290 MockResolver* mock_resolver = resolver.get();
291 proxy.resolver = std::move(resolver);
292 shill::Client::Device dev;
293 dev.state = shill::Client::Device::ConnectionState::kOnline;
294 EXPECT_CALL(*mock_resolver, Listen(_)).WillOnce(Return(true));
295 proxy.OnDefaultDeviceChanged(&dev);
296 EXPECT_TRUE(proxy.resolver_);
297}
298
299TEST_F(ProxyTest, CrashOnListenFailure) {
300 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
301 TestProxy proxy(Proxy::Options{.type = Proxy::Type::kSystem},
302 PatchpanelClient(), ShillClient());
303 proxy.device_ = std::make_unique<shill::Client::Device>();
304 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
305 auto resolver = std::make_unique<MockResolver>();
306 MockResolver* mock_resolver = resolver.get();
307 proxy.resolver = std::move(resolver);
308 shill::Client::Device dev;
309 dev.state = shill::Client::Device::ConnectionState::kOnline;
310 ON_CALL(*mock_resolver, Listen(_)).WillByDefault(Return(false));
311 EXPECT_DEATH(proxy.OnDefaultDeviceChanged(&dev), "relay loop");
312}
313
314TEST_F(ProxyTest, NameServersUpdatedOnDefaultServiceComesOnline) {
315 Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, PatchpanelClient(),
316 ShillClient());
317 proxy.device_ = std::make_unique<shill::Client::Device>();
318 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
319 auto resolver = std::make_unique<MockResolver>();
320 MockResolver* mock_resolver = resolver.get();
321 proxy.resolver_ = std::move(resolver);
322 shill::Client::Device dev;
323 dev.state = shill::Client::Device::ConnectionState::kOnline;
324 dev.ipconfig.ipv4_dns_addresses = {"a", "b"};
325 dev.ipconfig.ipv6_dns_addresses = {"c", "d"};
326 // Doesn't call listen since the resolver already exists.
327 EXPECT_CALL(*mock_resolver, Listen(_)).Times(0);
328 EXPECT_CALL(*mock_resolver,
329 SetNameServers(
330 ElementsAre(StrEq("a"), StrEq("b"), StrEq("c"), StrEq("d"))));
331 proxy.OnDefaultDeviceChanged(&dev);
332}
333
334TEST_F(ProxyTest, SystemProxy_ShillPropertyUpdatedOnDefaultServiceComesOnline) {
335 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
336 ShillClient());
337 proxy.device_ = std::make_unique<shill::Client::Device>();
338 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
339 auto resolver = std::make_unique<MockResolver>();
340 MockResolver* mock_resolver = resolver.get();
341 proxy.resolver_ = std::move(resolver);
342 shill::Client::Device dev;
343 dev.state = shill::Client::Device::ConnectionState::kOnline;
344 EXPECT_CALL(*mock_resolver, SetNameServers(_));
345 EXPECT_CALL(mock_manager_,
346 SetProperty(shill::kDNSProxyIPv4AddressProperty, _, _, _))
347 .WillOnce(Return(true));
348 proxy.OnDefaultDeviceChanged(&dev);
349}
350
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900351} // namespace dns_proxy