blob: f7ce86c2486ee28b39c609da8a7e3d1afb682743 [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
Jason Jeremy Iman6fd98552021-01-27 04:19:07 +090086 MOCK_METHOD(bool, ListenUDP, (struct sockaddr*), (override));
87 MOCK_METHOD(bool, ListenTCP, (struct sockaddr*), (override));
Garrick Evans2ca050d2021-02-09 18:21:36 +090088 MOCK_METHOD(void,
89 SetNameServers,
90 (const std::vector<std::string>&),
91 (override));
92 MOCK_METHOD(void,
93 SetDoHProviders,
94 (const std::vector<std::string>&, bool),
95 (override));
96};
97
98class TestProxy : public Proxy {
99 public:
100 TestProxy(const Options& opts,
101 std::unique_ptr<patchpanel::Client> patchpanel,
102 std::unique_ptr<shill::Client> shill)
103 : Proxy(opts, std::move(patchpanel), std::move(shill)) {}
104
105 std::unique_ptr<Resolver> resolver;
Jason Jeremy Iman1bb71c22021-01-26 21:49:55 +0900106 std::unique_ptr<Resolver> NewResolver(
107 base::TimeDelta request_timeout) override {
Garrick Evans2ca050d2021-02-09 18:21:36 +0900108 return std::move(resolver);
109 }
110};
111
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900112class ProxyTest : public ::testing::Test {
113 protected:
114 ProxyTest() : mock_bus_(new dbus::MockBus{dbus::Bus::Options{}}) {}
115 ~ProxyTest() { mock_bus_->ShutdownAndBlock(); }
116
117 std::unique_ptr<FakePatchpanelClient> PatchpanelClient() const {
118 return std::make_unique<FakePatchpanelClient>();
119 }
120
121 std::unique_ptr<FakeShillClient> ShillClient() const {
122 return std::make_unique<FakeShillClient>(
123 mock_bus_, reinterpret_cast<ManagerProxyInterface*>(
124 const_cast<ManagerProxyMock*>(&mock_manager_)));
125 }
126
127 int make_fd() const {
128 std::string fn(
129 ::testing::UnitTest::GetInstance()->current_test_info()->name());
130 fn = "/tmp/" + fn;
131 return open(fn.c_str(), O_CREAT, 0600);
132 }
133
134 protected:
135 scoped_refptr<dbus::MockBus> mock_bus_;
136 ManagerProxyMock mock_manager_;
137};
138
139TEST_F(ProxyTest, SystemProxy_OnShutdownClearsAddressPropertyOnShill) {
140 EXPECT_CALL(mock_manager_, SetProperty(shill::kDNSProxyIPv4AddressProperty,
141 brillo::Any(std::string()), _, _))
142 .WillOnce(Return(true));
143 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
144 ShillClient());
145 int unused;
146 proxy.OnShutdown(&unused);
147}
148
149TEST_F(ProxyTest, NonSystemProxy_OnShutdownDoesNotCallShill) {
150 EXPECT_CALL(mock_manager_, SetProperty(_, _, _, _)).Times(0);
151 Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, PatchpanelClient(),
152 ShillClient());
153 int unused;
154 proxy.OnShutdown(&unused);
155}
156
157TEST_F(ProxyTest, SystemProxy_SetShillPropertyWithNoRetriesCrashes) {
158 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
159 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
160 ShillClient());
161 EXPECT_DEATH(proxy.SetShillProperty("10.10.10.10", true, 0), "");
162}
163
164TEST_F(ProxyTest, SystemProxy_SetShillPropertyDoesntCrashIfDieFalse) {
165 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
166 EXPECT_CALL(mock_manager_, SetProperty(_, _, _, _)).Times(0);
167 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
168 ShillClient());
169 proxy.SetShillProperty("10.10.10.10", false, 0);
170}
171
172TEST_F(ProxyTest, SetupInitializesShill) {
173 auto shill = ShillClient();
174 auto* shill_ptr = shill.get();
175 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
176 std::move(shill));
177 proxy.Setup();
178 EXPECT_TRUE(shill_ptr->IsInitialized());
179}
180
181TEST_F(ProxyTest, SystemProxy_ConnectedNamedspace) {
182 auto pp = PatchpanelClient();
183 auto* pp_ptr = pp.get();
184 pp->SetConnectNamespaceResult(make_fd(),
185 patchpanel::ConnectNamespaceResponse());
186 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, std::move(pp),
187 ShillClient());
188 proxy.OnPatchpanelReady(true);
189 EXPECT_TRUE(pp_ptr->ns_ifname_.empty());
190 EXPECT_FALSE(pp_ptr->ns_rvpn_);
191 EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::SYSTEM);
192}
193
194TEST_F(ProxyTest, DefaultProxy_ConnectedNamedspace) {
195 auto pp = PatchpanelClient();
196 auto* pp_ptr = pp.get();
197 pp->SetConnectNamespaceResult(make_fd(),
198 patchpanel::ConnectNamespaceResponse());
199 Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(pp),
200 ShillClient());
201 proxy.OnPatchpanelReady(true);
202 EXPECT_TRUE(pp_ptr->ns_ifname_.empty());
203 EXPECT_TRUE(pp_ptr->ns_rvpn_);
204 EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::USER);
205}
206
207TEST_F(ProxyTest, ArcProxy_ConnectedNamedspace) {
208 auto pp = PatchpanelClient();
209 auto* pp_ptr = pp.get();
210 pp->SetConnectNamespaceResult(make_fd(),
211 patchpanel::ConnectNamespaceResponse());
212 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
213 std::move(pp), ShillClient());
214 proxy.OnPatchpanelReady(true);
215 EXPECT_EQ(pp_ptr->ns_ifname_, "eth0");
216 EXPECT_FALSE(pp_ptr->ns_rvpn_);
217 EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::ARC);
218}
219
220TEST_F(ProxyTest, CrashOnConnectNamespaceFailure) {
Garrick Evans2ca050d2021-02-09 18:21:36 +0900221 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900222 auto pp = PatchpanelClient();
223 pp->SetConnectNamespaceResult(-1 /* invalid fd */,
224 patchpanel::ConnectNamespaceResponse());
225 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
226 std::move(pp), ShillClient());
227 EXPECT_DEATH(proxy.OnPatchpanelReady(true), "namespace");
228}
229
230TEST_F(ProxyTest, CrashOnPatchpanelNotReady) {
Garrick Evans2ca050d2021-02-09 18:21:36 +0900231 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900232 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
233 PatchpanelClient(), ShillClient());
234 EXPECT_DEATH(proxy.OnPatchpanelReady(false), "patchpanel");
235}
236
237TEST_F(ProxyTest, ShillResetRestoresAddressProperty) {
238 auto pp = PatchpanelClient();
239 patchpanel::ConnectNamespaceResponse resp;
240 resp.set_host_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
241 pp->SetConnectNamespaceResult(make_fd(), resp);
242 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, std::move(pp),
243 ShillClient());
244 proxy.OnPatchpanelReady(true);
245 EXPECT_CALL(mock_manager_,
246 SetProperty(shill::kDNSProxyIPv4AddressProperty,
247 brillo::Any(std::string("10.10.10.10")), _, _))
248 .WillOnce(Return(true));
249 proxy.OnShillReset(true);
250}
251
Garrick Evans2ca050d2021-02-09 18:21:36 +0900252TEST_F(ProxyTest, StateClearedIfDefaultServiceDrops) {
253 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
254 ShillClient());
255 proxy.device_ = std::make_unique<shill::Client::Device>();
256 proxy.resolver_ = std::make_unique<MockResolver>();
257 proxy.OnDefaultDeviceChanged(nullptr /* no service */);
258 EXPECT_FALSE(proxy.device_);
259 EXPECT_FALSE(proxy.resolver_);
260}
261
262TEST_F(ProxyTest, ArcProxy_IgnoredIfDefaultServiceDrops) {
263 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC}, PatchpanelClient(),
264 ShillClient());
265 proxy.device_ = std::make_unique<shill::Client::Device>();
266 proxy.resolver_ = std::make_unique<MockResolver>();
267 proxy.OnDefaultDeviceChanged(nullptr /* no service */);
268 EXPECT_TRUE(proxy.device_);
269 EXPECT_TRUE(proxy.resolver_);
270}
271
272TEST_F(ProxyTest, StateClearedIfDefaultServiceIsNotOnline) {
273 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
274 ShillClient());
275 proxy.device_ = std::make_unique<shill::Client::Device>();
276 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
277 proxy.resolver_ = std::make_unique<MockResolver>();
278 shill::Client::Device dev;
279 dev.state = shill::Client::Device::ConnectionState::kReady;
280 proxy.OnDefaultDeviceChanged(&dev);
281 EXPECT_FALSE(proxy.device_);
282 EXPECT_FALSE(proxy.resolver_);
283}
284
285TEST_F(ProxyTest, NewResolverStartsListeningOnDefaultServiceComesOnline) {
286 TestProxy proxy(Proxy::Options{.type = Proxy::Type::kDefault},
287 PatchpanelClient(), ShillClient());
288 proxy.device_ = std::make_unique<shill::Client::Device>();
289 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
290 auto resolver = std::make_unique<MockResolver>();
291 MockResolver* mock_resolver = resolver.get();
292 proxy.resolver = std::move(resolver);
293 shill::Client::Device dev;
294 dev.state = shill::Client::Device::ConnectionState::kOnline;
Jason Jeremy Iman6fd98552021-01-27 04:19:07 +0900295 EXPECT_CALL(*mock_resolver, ListenUDP(_)).WillOnce(Return(true));
296 EXPECT_CALL(*mock_resolver, ListenTCP(_)).WillOnce(Return(true));
Garrick Evans2ca050d2021-02-09 18:21:36 +0900297 proxy.OnDefaultDeviceChanged(&dev);
298 EXPECT_TRUE(proxy.resolver_);
299}
300
301TEST_F(ProxyTest, CrashOnListenFailure) {
302 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
303 TestProxy proxy(Proxy::Options{.type = Proxy::Type::kSystem},
304 PatchpanelClient(), ShillClient());
305 proxy.device_ = std::make_unique<shill::Client::Device>();
306 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
307 auto resolver = std::make_unique<MockResolver>();
308 MockResolver* mock_resolver = resolver.get();
309 proxy.resolver = std::move(resolver);
310 shill::Client::Device dev;
311 dev.state = shill::Client::Device::ConnectionState::kOnline;
Jason Jeremy Iman6fd98552021-01-27 04:19:07 +0900312 ON_CALL(*mock_resolver, ListenUDP(_)).WillByDefault(Return(false));
313 ON_CALL(*mock_resolver, ListenTCP(_)).WillByDefault(Return(false));
Garrick Evans2ca050d2021-02-09 18:21:36 +0900314 EXPECT_DEATH(proxy.OnDefaultDeviceChanged(&dev), "relay loop");
315}
316
317TEST_F(ProxyTest, NameServersUpdatedOnDefaultServiceComesOnline) {
318 Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, PatchpanelClient(),
319 ShillClient());
320 proxy.device_ = std::make_unique<shill::Client::Device>();
321 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
322 auto resolver = std::make_unique<MockResolver>();
323 MockResolver* mock_resolver = resolver.get();
324 proxy.resolver_ = std::move(resolver);
325 shill::Client::Device dev;
326 dev.state = shill::Client::Device::ConnectionState::kOnline;
327 dev.ipconfig.ipv4_dns_addresses = {"a", "b"};
328 dev.ipconfig.ipv6_dns_addresses = {"c", "d"};
329 // Doesn't call listen since the resolver already exists.
Jason Jeremy Iman6fd98552021-01-27 04:19:07 +0900330 EXPECT_CALL(*mock_resolver, ListenUDP(_)).Times(0);
331 EXPECT_CALL(*mock_resolver, ListenTCP(_)).Times(0);
Garrick Evans2ca050d2021-02-09 18:21:36 +0900332 EXPECT_CALL(*mock_resolver,
333 SetNameServers(
334 ElementsAre(StrEq("a"), StrEq("b"), StrEq("c"), StrEq("d"))));
335 proxy.OnDefaultDeviceChanged(&dev);
336}
337
338TEST_F(ProxyTest, SystemProxy_ShillPropertyUpdatedOnDefaultServiceComesOnline) {
339 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
340 ShillClient());
341 proxy.device_ = std::make_unique<shill::Client::Device>();
342 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
343 auto resolver = std::make_unique<MockResolver>();
344 MockResolver* mock_resolver = resolver.get();
345 proxy.resolver_ = std::move(resolver);
346 shill::Client::Device dev;
347 dev.state = shill::Client::Device::ConnectionState::kOnline;
348 EXPECT_CALL(*mock_resolver, SetNameServers(_));
349 EXPECT_CALL(mock_manager_,
350 SetProperty(shill::kDNSProxyIPv4AddressProperty, _, _, _))
351 .WillOnce(Return(true));
352 proxy.OnDefaultDeviceChanged(&dev);
353}
354
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900355} // namespace dns_proxy