blob: fad8490a266598753e0d5fa54168984a6e9887e6 [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);
Jason Jeremy Iman845f2932021-01-31 16:12:13 +090026constexpr base::TimeDelta kRequestRetryDelay =
27 base::TimeDelta::FromMilliseconds(200);
28constexpr int32_t kRequestMaxRetry = 1;
29
Jason Jeremy Iman1bb71c22021-01-26 21:49:55 +090030} // namespace
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090031using org::chromium::flimflam::ManagerProxyInterface;
32using org::chromium::flimflam::ManagerProxyMock;
33using testing::_;
34using testing::Return;
Garrick Evansadde9852021-02-15 20:16:53 +090035using testing::SetArgPointee;
Garrick Evans2ca050d2021-02-09 18:21:36 +090036using testing::StrEq;
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090037
38class FakeShillClient : public shill::FakeClient {
39 public:
40 FakeShillClient(scoped_refptr<dbus::Bus> bus,
41 ManagerProxyInterface* manager_proxy)
42 : shill::FakeClient(bus), manager_proxy_(manager_proxy) {}
43
44 std::unique_ptr<shill::Client::ManagerPropertyAccessor> ManagerProperties(
45 const base::TimeDelta& timeout) const override {
46 return std::make_unique<shill::Client::ManagerPropertyAccessor>(
47 manager_proxy_);
48 }
49
Garrick Evansadde9852021-02-15 20:16:53 +090050 std::unique_ptr<shill::Client::Device> DefaultDevice(
51 bool exclude_vpn) override {
52 return std::move(default_device_);
53 }
54
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090055 bool IsInitialized() const { return init_; }
56
Garrick Evansadde9852021-02-15 20:16:53 +090057 std::unique_ptr<shill::Client::Device> default_device_;
58
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090059 private:
60 ManagerProxyInterface* manager_proxy_;
61};
62
63class FakePatchpanelClient : public patchpanel::FakeClient {
64 public:
65 FakePatchpanelClient() = default;
66 ~FakePatchpanelClient() = default;
67
68 void SetConnectNamespaceResult(
69 int fd, const patchpanel::ConnectNamespaceResponse& resp) {
70 ns_fd_ = fd;
71 ns_resp_ = resp;
72 }
73
74 std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse>
75 ConnectNamespace(pid_t pid,
76 const std::string& outbound_ifname,
77 bool forward_user_traffic,
78 bool route_on_vpn,
79 patchpanel::TrafficCounter::Source traffic_source) override {
80 ns_ifname_ = outbound_ifname;
81 ns_rvpn_ = route_on_vpn;
82 ns_ts_ = traffic_source;
83 return {base::ScopedFD(ns_fd_), ns_resp_};
84 }
85
86 std::string ns_ifname_;
87 bool ns_rvpn_;
88 patchpanel::TrafficCounter::Source ns_ts_;
89 int ns_fd_;
90 patchpanel::ConnectNamespaceResponse ns_resp_;
91};
92
Garrick Evans2ca050d2021-02-09 18:21:36 +090093class MockResolver : public Resolver {
94 public:
Jason Jeremy Iman845f2932021-01-31 16:12:13 +090095 MockResolver()
96 : Resolver(kRequestTimeout, kRequestRetryDelay, kRequestMaxRetry) {}
Garrick Evans2ca050d2021-02-09 18:21:36 +090097 ~MockResolver() = default;
98
Jason Jeremy Iman6fd98552021-01-27 04:19:07 +090099 MOCK_METHOD(bool, ListenUDP, (struct sockaddr*), (override));
100 MOCK_METHOD(bool, ListenTCP, (struct sockaddr*), (override));
Garrick Evans2ca050d2021-02-09 18:21:36 +0900101 MOCK_METHOD(void,
102 SetNameServers,
103 (const std::vector<std::string>&),
104 (override));
105 MOCK_METHOD(void,
106 SetDoHProviders,
107 (const std::vector<std::string>&, bool),
108 (override));
109};
110
111class TestProxy : public Proxy {
112 public:
113 TestProxy(const Options& opts,
114 std::unique_ptr<patchpanel::Client> patchpanel,
115 std::unique_ptr<shill::Client> shill)
116 : Proxy(opts, std::move(patchpanel), std::move(shill)) {}
117
118 std::unique_ptr<Resolver> resolver;
Jason Jeremy Iman845f2932021-01-31 16:12:13 +0900119 std::unique_ptr<Resolver> NewResolver(base::TimeDelta timeout,
120 base::TimeDelta retry_delay,
121 int max_num_retries) override {
Garrick Evans2ca050d2021-02-09 18:21:36 +0900122 return std::move(resolver);
123 }
124};
125
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900126class ProxyTest : public ::testing::Test {
127 protected:
128 ProxyTest() : mock_bus_(new dbus::MockBus{dbus::Bus::Options{}}) {}
129 ~ProxyTest() { mock_bus_->ShutdownAndBlock(); }
130
131 std::unique_ptr<FakePatchpanelClient> PatchpanelClient() const {
132 return std::make_unique<FakePatchpanelClient>();
133 }
134
135 std::unique_ptr<FakeShillClient> ShillClient() const {
136 return std::make_unique<FakeShillClient>(
137 mock_bus_, reinterpret_cast<ManagerProxyInterface*>(
138 const_cast<ManagerProxyMock*>(&mock_manager_)));
139 }
140
141 int make_fd() const {
142 std::string fn(
143 ::testing::UnitTest::GetInstance()->current_test_info()->name());
144 fn = "/tmp/" + fn;
145 return open(fn.c_str(), O_CREAT, 0600);
146 }
147
148 protected:
149 scoped_refptr<dbus::MockBus> mock_bus_;
150 ManagerProxyMock mock_manager_;
151};
152
153TEST_F(ProxyTest, SystemProxy_OnShutdownClearsAddressPropertyOnShill) {
154 EXPECT_CALL(mock_manager_, SetProperty(shill::kDNSProxyIPv4AddressProperty,
155 brillo::Any(std::string()), _, _))
156 .WillOnce(Return(true));
157 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
158 ShillClient());
159 int unused;
160 proxy.OnShutdown(&unused);
161}
162
163TEST_F(ProxyTest, NonSystemProxy_OnShutdownDoesNotCallShill) {
164 EXPECT_CALL(mock_manager_, SetProperty(_, _, _, _)).Times(0);
165 Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, PatchpanelClient(),
166 ShillClient());
167 int unused;
168 proxy.OnShutdown(&unused);
169}
170
171TEST_F(ProxyTest, SystemProxy_SetShillPropertyWithNoRetriesCrashes) {
172 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
173 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
174 ShillClient());
175 EXPECT_DEATH(proxy.SetShillProperty("10.10.10.10", true, 0), "");
176}
177
178TEST_F(ProxyTest, SystemProxy_SetShillPropertyDoesntCrashIfDieFalse) {
179 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
180 EXPECT_CALL(mock_manager_, SetProperty(_, _, _, _)).Times(0);
181 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
182 ShillClient());
183 proxy.SetShillProperty("10.10.10.10", false, 0);
184}
185
Garrick Evansab03c462021-02-15 20:54:20 +0900186TEST_F(ProxyTest, ShillInitializedWhenReady) {
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900187 auto shill = ShillClient();
188 auto* shill_ptr = shill.get();
189 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
190 std::move(shill));
Garrick Evansab03c462021-02-15 20:54:20 +0900191 proxy.OnShillReady(true);
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900192 EXPECT_TRUE(shill_ptr->IsInitialized());
193}
194
195TEST_F(ProxyTest, SystemProxy_ConnectedNamedspace) {
196 auto pp = PatchpanelClient();
197 auto* pp_ptr = pp.get();
198 pp->SetConnectNamespaceResult(make_fd(),
199 patchpanel::ConnectNamespaceResponse());
200 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, std::move(pp),
201 ShillClient());
202 proxy.OnPatchpanelReady(true);
203 EXPECT_TRUE(pp_ptr->ns_ifname_.empty());
204 EXPECT_FALSE(pp_ptr->ns_rvpn_);
205 EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::SYSTEM);
206}
207
208TEST_F(ProxyTest, DefaultProxy_ConnectedNamedspace) {
209 auto pp = PatchpanelClient();
210 auto* pp_ptr = pp.get();
211 pp->SetConnectNamespaceResult(make_fd(),
212 patchpanel::ConnectNamespaceResponse());
213 Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, std::move(pp),
214 ShillClient());
215 proxy.OnPatchpanelReady(true);
216 EXPECT_TRUE(pp_ptr->ns_ifname_.empty());
217 EXPECT_TRUE(pp_ptr->ns_rvpn_);
218 EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::USER);
219}
220
221TEST_F(ProxyTest, ArcProxy_ConnectedNamedspace) {
222 auto pp = PatchpanelClient();
223 auto* pp_ptr = pp.get();
224 pp->SetConnectNamespaceResult(make_fd(),
225 patchpanel::ConnectNamespaceResponse());
226 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
227 std::move(pp), ShillClient());
228 proxy.OnPatchpanelReady(true);
229 EXPECT_EQ(pp_ptr->ns_ifname_, "eth0");
230 EXPECT_FALSE(pp_ptr->ns_rvpn_);
231 EXPECT_EQ(pp_ptr->ns_ts_, patchpanel::TrafficCounter::ARC);
232}
233
234TEST_F(ProxyTest, CrashOnConnectNamespaceFailure) {
Garrick Evans2ca050d2021-02-09 18:21:36 +0900235 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900236 auto pp = PatchpanelClient();
237 pp->SetConnectNamespaceResult(-1 /* invalid fd */,
238 patchpanel::ConnectNamespaceResponse());
239 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
240 std::move(pp), ShillClient());
241 EXPECT_DEATH(proxy.OnPatchpanelReady(true), "namespace");
242}
243
244TEST_F(ProxyTest, CrashOnPatchpanelNotReady) {
Garrick Evans2ca050d2021-02-09 18:21:36 +0900245 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900246 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
247 PatchpanelClient(), ShillClient());
248 EXPECT_DEATH(proxy.OnPatchpanelReady(false), "patchpanel");
249}
250
251TEST_F(ProxyTest, ShillResetRestoresAddressProperty) {
252 auto pp = PatchpanelClient();
253 patchpanel::ConnectNamespaceResponse resp;
Garrick Evansab03c462021-02-15 20:54:20 +0900254 resp.set_peer_ipv4_address(patchpanel::Ipv4Addr(10, 10, 10, 10));
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900255 pp->SetConnectNamespaceResult(make_fd(), resp);
256 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, std::move(pp),
257 ShillClient());
258 proxy.OnPatchpanelReady(true);
259 EXPECT_CALL(mock_manager_,
260 SetProperty(shill::kDNSProxyIPv4AddressProperty,
261 brillo::Any(std::string("10.10.10.10")), _, _))
262 .WillOnce(Return(true));
263 proxy.OnShillReset(true);
264}
265
Garrick Evans2ca050d2021-02-09 18:21:36 +0900266TEST_F(ProxyTest, StateClearedIfDefaultServiceDrops) {
267 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
268 ShillClient());
269 proxy.device_ = std::make_unique<shill::Client::Device>();
270 proxy.resolver_ = std::make_unique<MockResolver>();
271 proxy.OnDefaultDeviceChanged(nullptr /* no service */);
272 EXPECT_FALSE(proxy.device_);
273 EXPECT_FALSE(proxy.resolver_);
274}
275
276TEST_F(ProxyTest, ArcProxy_IgnoredIfDefaultServiceDrops) {
277 Proxy proxy(Proxy::Options{.type = Proxy::Type::kARC}, PatchpanelClient(),
278 ShillClient());
279 proxy.device_ = std::make_unique<shill::Client::Device>();
280 proxy.resolver_ = std::make_unique<MockResolver>();
281 proxy.OnDefaultDeviceChanged(nullptr /* no service */);
282 EXPECT_TRUE(proxy.device_);
283 EXPECT_TRUE(proxy.resolver_);
284}
285
286TEST_F(ProxyTest, StateClearedIfDefaultServiceIsNotOnline) {
287 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
288 ShillClient());
289 proxy.device_ = std::make_unique<shill::Client::Device>();
290 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
291 proxy.resolver_ = std::make_unique<MockResolver>();
292 shill::Client::Device dev;
293 dev.state = shill::Client::Device::ConnectionState::kReady;
294 proxy.OnDefaultDeviceChanged(&dev);
295 EXPECT_FALSE(proxy.device_);
296 EXPECT_FALSE(proxy.resolver_);
297}
298
299TEST_F(ProxyTest, NewResolverStartsListeningOnDefaultServiceComesOnline) {
300 TestProxy proxy(Proxy::Options{.type = Proxy::Type::kDefault},
301 PatchpanelClient(), ShillClient());
302 proxy.device_ = std::make_unique<shill::Client::Device>();
303 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
304 auto resolver = std::make_unique<MockResolver>();
305 MockResolver* mock_resolver = resolver.get();
306 proxy.resolver = std::move(resolver);
307 shill::Client::Device dev;
308 dev.state = shill::Client::Device::ConnectionState::kOnline;
Jason Jeremy Iman6fd98552021-01-27 04:19:07 +0900309 EXPECT_CALL(*mock_resolver, ListenUDP(_)).WillOnce(Return(true));
310 EXPECT_CALL(*mock_resolver, ListenTCP(_)).WillOnce(Return(true));
Garrick Evans2ca050d2021-02-09 18:21:36 +0900311 proxy.OnDefaultDeviceChanged(&dev);
312 EXPECT_TRUE(proxy.resolver_);
313}
314
315TEST_F(ProxyTest, CrashOnListenFailure) {
316 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
317 TestProxy proxy(Proxy::Options{.type = Proxy::Type::kSystem},
318 PatchpanelClient(), ShillClient());
319 proxy.device_ = std::make_unique<shill::Client::Device>();
320 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
321 auto resolver = std::make_unique<MockResolver>();
322 MockResolver* mock_resolver = resolver.get();
323 proxy.resolver = std::move(resolver);
324 shill::Client::Device dev;
325 dev.state = shill::Client::Device::ConnectionState::kOnline;
Jason Jeremy Iman6fd98552021-01-27 04:19:07 +0900326 ON_CALL(*mock_resolver, ListenUDP(_)).WillByDefault(Return(false));
327 ON_CALL(*mock_resolver, ListenTCP(_)).WillByDefault(Return(false));
Garrick Evans2ca050d2021-02-09 18:21:36 +0900328 EXPECT_DEATH(proxy.OnDefaultDeviceChanged(&dev), "relay loop");
329}
330
331TEST_F(ProxyTest, NameServersUpdatedOnDefaultServiceComesOnline) {
332 Proxy proxy(Proxy::Options{.type = Proxy::Type::kDefault}, PatchpanelClient(),
333 ShillClient());
334 proxy.device_ = std::make_unique<shill::Client::Device>();
335 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
336 auto resolver = std::make_unique<MockResolver>();
337 MockResolver* mock_resolver = resolver.get();
338 proxy.resolver_ = std::move(resolver);
339 shill::Client::Device dev;
340 dev.state = shill::Client::Device::ConnectionState::kOnline;
341 dev.ipconfig.ipv4_dns_addresses = {"a", "b"};
342 dev.ipconfig.ipv6_dns_addresses = {"c", "d"};
343 // Doesn't call listen since the resolver already exists.
Jason Jeremy Iman6fd98552021-01-27 04:19:07 +0900344 EXPECT_CALL(*mock_resolver, ListenUDP(_)).Times(0);
345 EXPECT_CALL(*mock_resolver, ListenTCP(_)).Times(0);
Garrick Evans2ca050d2021-02-09 18:21:36 +0900346 EXPECT_CALL(*mock_resolver,
347 SetNameServers(
348 ElementsAre(StrEq("a"), StrEq("b"), StrEq("c"), StrEq("d"))));
349 proxy.OnDefaultDeviceChanged(&dev);
350}
351
352TEST_F(ProxyTest, SystemProxy_ShillPropertyUpdatedOnDefaultServiceComesOnline) {
353 Proxy proxy(Proxy::Options{.type = Proxy::Type::kSystem}, PatchpanelClient(),
354 ShillClient());
355 proxy.device_ = std::make_unique<shill::Client::Device>();
356 proxy.device_->state = shill::Client::Device::ConnectionState::kOnline;
357 auto resolver = std::make_unique<MockResolver>();
358 MockResolver* mock_resolver = resolver.get();
359 proxy.resolver_ = std::move(resolver);
360 shill::Client::Device dev;
361 dev.state = shill::Client::Device::ConnectionState::kOnline;
362 EXPECT_CALL(*mock_resolver, SetNameServers(_));
363 EXPECT_CALL(mock_manager_,
364 SetProperty(shill::kDNSProxyIPv4AddressProperty, _, _, _))
365 .WillOnce(Return(true));
366 proxy.OnDefaultDeviceChanged(&dev);
367}
368
Garrick Evansadde9852021-02-15 20:16:53 +0900369TEST_F(ProxyTest, SystemProxy_IgnoresVPN) {
370 TestProxy proxy(Proxy::Options{.type = Proxy::Type::kSystem},
371 PatchpanelClient(), ShillClient());
372 auto resolver = std::make_unique<MockResolver>();
373 MockResolver* mock_resolver = resolver.get();
374 ON_CALL(*mock_resolver, ListenUDP(_)).WillByDefault(Return(true));
375 ON_CALL(*mock_resolver, ListenTCP(_)).WillByDefault(Return(true));
376 EXPECT_CALL(mock_manager_,
377 SetProperty(shill::kDNSProxyIPv4AddressProperty, _, _, _))
378 .WillOnce(Return(true));
379 proxy.resolver = std::move(resolver);
380 shill::Client::Device dev;
381 dev.type = shill::Client::Device::Type::kWifi;
382 dev.state = shill::Client::Device::ConnectionState::kOnline;
383 proxy.OnDefaultDeviceChanged(&dev);
384 EXPECT_TRUE(proxy.device_);
385 EXPECT_EQ(proxy.device_->type, shill::Client::Device::Type::kWifi);
386 dev.type = shill::Client::Device::Type::kVPN;
387 proxy.OnDefaultDeviceChanged(&dev);
388 EXPECT_TRUE(proxy.device_);
389 EXPECT_EQ(proxy.device_->type, shill::Client::Device::Type::kWifi);
390}
391
392TEST_F(ProxyTest, SystemProxy_GetsPhysicalDeviceOnInitialVPN) {
393 auto shill = ShillClient();
394 auto* shill_ptr = shill.get();
395 TestProxy proxy(Proxy::Options{.type = Proxy::Type::kSystem},
396 PatchpanelClient(), std::move(shill));
397 auto resolver = std::make_unique<MockResolver>();
398 MockResolver* mock_resolver = resolver.get();
399 ON_CALL(*mock_resolver, ListenUDP(_)).WillByDefault(Return(true));
400 ON_CALL(*mock_resolver, ListenTCP(_)).WillByDefault(Return(true));
401 EXPECT_CALL(mock_manager_,
402 SetProperty(shill::kDNSProxyIPv4AddressProperty, _, _, _))
403 .WillOnce(Return(true));
404 proxy.resolver = std::move(resolver);
405 shill::Client::Device vpn;
406 vpn.type = shill::Client::Device::Type::kVPN;
407 vpn.state = shill::Client::Device::ConnectionState::kOnline;
408 shill_ptr->default_device_ = std::make_unique<shill::Client::Device>();
409 shill_ptr->default_device_->type = shill::Client::Device::Type::kWifi;
410 shill_ptr->default_device_->state =
411 shill::Client::Device::ConnectionState::kOnline;
412 proxy.OnDefaultDeviceChanged(&vpn);
413 EXPECT_TRUE(proxy.device_);
414 EXPECT_EQ(proxy.device_->type, shill::Client::Device::Type::kWifi);
415}
416
417TEST_F(ProxyTest, DefaultProxy_UsesVPN) {
418 TestProxy proxy(Proxy::Options{.type = Proxy::Type::kDefault},
419 PatchpanelClient(), ShillClient());
420 auto resolver = std::make_unique<MockResolver>();
421 MockResolver* mock_resolver = resolver.get();
422 ON_CALL(*mock_resolver, ListenUDP(_)).WillByDefault(Return(true));
423 ON_CALL(*mock_resolver, ListenTCP(_)).WillByDefault(Return(true));
424 proxy.resolver = std::move(resolver);
425 shill::Client::Device dev;
426 dev.type = shill::Client::Device::Type::kWifi;
427 dev.state = shill::Client::Device::ConnectionState::kOnline;
428 proxy.OnDefaultDeviceChanged(&dev);
429 EXPECT_TRUE(proxy.device_);
430 EXPECT_EQ(proxy.device_->type, shill::Client::Device::Type::kWifi);
431 dev.type = shill::Client::Device::Type::kVPN;
432 proxy.OnDefaultDeviceChanged(&dev);
433 EXPECT_TRUE(proxy.device_);
434 EXPECT_EQ(proxy.device_->type, shill::Client::Device::Type::kVPN);
435}
436
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900437} // namespace dns_proxy