blob: d42c0dfba8d8d580358e911c8679e470b777d435 [file] [log] [blame]
Garrick Evans066dc2c2020-12-10 10:43:55 +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 <sys/types.h>
Garrick Evans4f5428c2021-02-15 11:23:54 +09008#include <sysexits.h>
Garrick Evans066dc2c2020-12-10 10:43:55 +09009#include <unistd.h>
10
Garrick Evansd41fdbf2021-03-03 09:15:48 +090011#include <set>
Garrick Evans066dc2c2020-12-10 10:43:55 +090012#include <utility>
13
14#include <base/bind.h>
Qijiang Fan713061e2021-03-08 15:45:12 +090015#include <base/check.h>
Garrick Evansd41fdbf2021-03-03 09:15:48 +090016#include <base/strings/string_split.h>
Garrick Evans066dc2c2020-12-10 10:43:55 +090017#include <base/threading/thread_task_runner_handle.h>
Garrick Evans9c7afb82021-01-29 22:38:03 +090018#include <base/time/time.h>
Garrick Evans48c84ef2021-01-28 11:29:42 +090019#include <chromeos/patchpanel/net_util.h>
20#include <shill/dbus-constants.h>
Garrick Evans066dc2c2020-12-10 10:43:55 +090021
22namespace dns_proxy {
Garrick Evans4e1fc312021-05-10 14:47:31 +090023namespace {
24// The DoH provider URLs that come from Chrome may be URI templates instead.
25// Per https://datatracker.ietf.org/doc/html/rfc8484#section-4.1 these will
26// include the {?dns} parameter template for GET requests. These can be safely
27// removed since any compliant server must support both GET and POST requests
28// and this services only uses POST.
29constexpr char kDNSParamTemplate[] = "{?dns}";
30std::string TrimParamTemplate(const std::string& url) {
31 const size_t pos = url.find(kDNSParamTemplate);
32 if (pos == std::string::npos) {
33 return url;
34 }
35 return url.substr(0, pos);
36}
37} // namespace
Garrick Evans066dc2c2020-12-10 10:43:55 +090038
Garrick Evans9c7afb82021-01-29 22:38:03 +090039constexpr base::TimeDelta kShillPropertyAttemptDelay =
40 base::TimeDelta::FromMilliseconds(200);
Jason Jeremy Iman1bb71c22021-01-26 21:49:55 +090041constexpr base::TimeDelta kRequestTimeout = base::TimeDelta::FromSeconds(10000);
Jason Jeremy Iman845f2932021-01-31 16:12:13 +090042constexpr base::TimeDelta kRequestRetryDelay =
43 base::TimeDelta::FromMilliseconds(200);
Garrick Evans9c7afb82021-01-29 22:38:03 +090044
Garrick Evans066dc2c2020-12-10 10:43:55 +090045constexpr char kSystemProxyType[] = "sys";
46constexpr char kDefaultProxyType[] = "def";
47constexpr char kARCProxyType[] = "arc";
Jason Jeremy Iman845f2932021-01-31 16:12:13 +090048constexpr int32_t kRequestMaxRetry = 1;
Garrick Evans34650b32021-02-03 09:24:35 +090049constexpr uint16_t kDefaultPort = 13568; // port 53 in network order.
Garrick Evans304a5f42021-02-15 20:34:55 +090050constexpr char kIfAddrAny[] = "0.0.0.0";
51
Garrick Evans066dc2c2020-12-10 10:43:55 +090052// static
53const char* Proxy::TypeToString(Type t) {
54 switch (t) {
55 case Type::kSystem:
56 return kSystemProxyType;
57 case Type::kDefault:
58 return kDefaultProxyType;
59 case Type::kARC:
60 return kARCProxyType;
61 }
62}
63
64// static
65std::optional<Proxy::Type> Proxy::StringToType(const std::string& s) {
66 if (s == kSystemProxyType)
67 return Type::kSystem;
68
69 if (s == kDefaultProxyType)
70 return Type::kDefault;
71
72 if (s == kARCProxyType)
73 return Type::kARC;
74
75 return std::nullopt;
76}
77
78std::ostream& operator<<(std::ostream& stream, Proxy::Type type) {
79 stream << Proxy::TypeToString(type);
80 return stream;
81}
82
83std::ostream& operator<<(std::ostream& stream, Proxy::Options opt) {
84 stream << "{" << Proxy::TypeToString(opt.type) << ":" << opt.ifname << "}";
85 return stream;
86}
87
88Proxy::Proxy(const Proxy::Options& opts) : opts_(opts) {}
89
Garrick Evans77e9a132021-05-13 10:51:13 +090090// This ctor is only used for testing.
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090091Proxy::Proxy(const Options& opts,
92 std::unique_ptr<patchpanel::Client> patchpanel,
93 std::unique_ptr<shill::Client> shill)
94 : opts_(opts),
95 patchpanel_(std::move(patchpanel)),
Garrick Evans77e9a132021-05-13 10:51:13 +090096 shill_(std::move(shill)),
97 feature_enabled_(true) {}
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090098
Garrick Evans9c8797d2021-02-17 21:28:10 +090099Proxy::~Proxy() {
100 if (bus_)
101 bus_->ShutdownAndBlock();
102}
103
Garrick Evans066dc2c2020-12-10 10:43:55 +0900104int Proxy::OnInit() {
105 LOG(INFO) << "Starting DNS proxy " << opts_;
106
107 /// Run after Daemon::OnInit()
108 base::ThreadTaskRunnerHandle::Get()->PostTask(
109 FROM_HERE, base::Bind(&Proxy::Setup, weak_factory_.GetWeakPtr()));
110 return DBusDaemon::OnInit();
111}
112
113void Proxy::OnShutdown(int*) {
114 LOG(INFO) << "Stopping DNS proxy " << opts_;
Garrick Evans34650b32021-02-03 09:24:35 +0900115 if (opts_.type == Type::kSystem)
116 SetShillProperty("");
Garrick Evans066dc2c2020-12-10 10:43:55 +0900117}
118
119void Proxy::Setup() {
Garrick Evans77e9a132021-05-13 10:51:13 +0900120 if (!session_) {
121 session_ = std::make_unique<SessionMonitor>(bus_);
122 }
123 session_->RegisterSessionStateHandler(base::BindRepeating(
124 &Proxy::OnSessionStateChanged, weak_factory_.GetWeakPtr()));
125
126 if (!features_) {
127 features_ = ChromeFeaturesServiceClient::New(bus_);
128
129 if (!features_) {
130 LOG(DFATAL) << "Failed to initialize Chrome features client";
131 return;
132 }
133 }
134 features_->IsDNSProxyEnabled(
135 base::BindOnce(&Proxy::OnFeatureEnabled, weak_factory_.GetWeakPtr()));
136
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900137 if (!patchpanel_)
138 patchpanel_ = patchpanel::Client::New();
139
Garrick Evans066dc2c2020-12-10 10:43:55 +0900140 CHECK(patchpanel_) << "Failed to initialize patchpanel client";
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900141
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900142 if (!shill_)
143 shill_.reset(new shill::Client(bus_));
144
Garrick Evans066dc2c2020-12-10 10:43:55 +0900145 patchpanel_->RegisterOnAvailableCallback(base::BindRepeating(
146 &Proxy::OnPatchpanelReady, weak_factory_.GetWeakPtr()));
Garrick Evans4f5428c2021-02-15 11:23:54 +0900147 patchpanel_->RegisterProcessChangedCallback(base::BindRepeating(
148 &Proxy::OnPatchpanelReset, weak_factory_.GetWeakPtr()));
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900149
150 shill_->RegisterOnAvailableCallback(
151 base::BindOnce(&Proxy::OnShillReady, weak_factory_.GetWeakPtr()));
Garrick Evans066dc2c2020-12-10 10:43:55 +0900152}
153
154void Proxy::OnPatchpanelReady(bool success) {
155 CHECK(success) << "Failed to connect to patchpanel";
156
157 // The default network proxy might actually be carrying Chrome, Crostini or
158 // if a VPN is on, even ARC traffic, but we attribute this as as "user"
159 // sourced.
160 patchpanel::TrafficCounter::Source traffic_source;
161 switch (opts_.type) {
162 case Type::kSystem:
163 traffic_source = patchpanel::TrafficCounter::SYSTEM;
164 break;
165 case Type::kARC:
166 traffic_source = patchpanel::TrafficCounter::ARC;
167 break;
168 default:
169 traffic_source = patchpanel::TrafficCounter::USER;
170 }
171
172 // Note that using getpid() here requires that this minijail is not creating a
173 // new PID namespace.
174 // The default proxy (only) needs to use the VPN, if applicable, the others
175 // expressly need to avoid it.
176 auto res = patchpanel_->ConnectNamespace(
177 getpid(), opts_.ifname, true /* forward_user_traffic */,
178 opts_.type == Type::kDefault /* route_on_vpn */, traffic_source);
179 CHECK(res.first.is_valid())
180 << "Failed to establish private network namespace";
181 ns_fd_ = std::move(res.first);
Garrick Evans9c7afb82021-01-29 22:38:03 +0900182 ns_ = res.second;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900183 LOG(INFO) << "Sucessfully connected private network namespace:"
Garrick Evans9c7afb82021-01-29 22:38:03 +0900184 << ns_.host_ifname() << " <--> " << ns_.peer_ifname();
Garrick Evans48c84ef2021-01-28 11:29:42 +0900185
Garrick Evans34650b32021-02-03 09:24:35 +0900186 // Now it's safe to register these handlers and respond to them.
187 shill_->RegisterDefaultDeviceChangedHandler(base::BindRepeating(
188 &Proxy::OnDefaultDeviceChanged, weak_factory_.GetWeakPtr()));
189 shill_->RegisterDeviceChangedHandler(
190 base::BindRepeating(&Proxy::OnDeviceChanged, weak_factory_.GetWeakPtr()));
191
Garrick Evans48c84ef2021-01-28 11:29:42 +0900192 if (opts_.type == Type::kSystem)
Garrick Evans34650b32021-02-03 09:24:35 +0900193 shill_->RegisterProcessChangedHandler(
194 base::BindRepeating(&Proxy::OnShillReset, weak_factory_.GetWeakPtr()));
Garrick Evans9c7afb82021-01-29 22:38:03 +0900195}
196
Garrick Evans4f5428c2021-02-15 11:23:54 +0900197void Proxy::OnPatchpanelReset(bool reset) {
198 // If patchpanel crashes, the proxy is useless since the connected virtual
199 // network is gone. So the best bet is to exit and have the controller restart
200 // us. Note if this is the system proxy, it will inform shill on shutdown.
201 LOG(ERROR) << "Patchpanel has been shutdown - restarting DNS proxy " << opts_;
202 QuitWithExitCode(EX_UNAVAILABLE);
203
204 LOG(WARNING) << "Patchpanel has been reset";
205}
206
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900207void Proxy::OnShillReady(bool success) {
208 CHECK(success) << "Failed to connect to shill";
209 shill_->Init();
210}
211
Garrick Evans9c7afb82021-01-29 22:38:03 +0900212void Proxy::OnShillReset(bool reset) {
213 if (!reset) {
214 LOG(WARNING) << "Shill has been shutdown";
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900215 // Watch for it to return.
216 shill_->RegisterOnAvailableCallback(
217 base::BindOnce(&Proxy::OnShillReady, weak_factory_.GetWeakPtr()));
Garrick Evans9c7afb82021-01-29 22:38:03 +0900218 return;
219 }
220
Garrick Evans34650b32021-02-03 09:24:35 +0900221 // Really this means shill crashed. To be safe, explicitly reset the proxy
222 // address. We don't want to crash on failure here because shill might still
223 // have this address and try to use it. This probably redundant though with us
224 // rediscovering the default device.
225 // TODO(garrick): Remove this if so.
Garrick Evans9c7afb82021-01-29 22:38:03 +0900226 LOG(WARNING) << "Shill has been reset";
Garrick Evans77e9a132021-05-13 10:51:13 +0900227 if (ns_fd_.is_valid())
228 SetShillProperty(patchpanel::IPv4AddressToString(ns_.peer_ipv4_address()));
229}
230
231void Proxy::OnSessionStateChanged(bool login) {
232 if (login) {
233 features_->IsDNSProxyEnabled(
234 base::BindOnce(&Proxy::OnFeatureEnabled, weak_factory_.GetWeakPtr()));
235 return;
236 }
237
238 LOG(INFO) << "Service disabled by user logout";
239 Disable();
240}
241
242void Proxy::OnFeatureEnabled(base::Optional<bool> enabled) {
243 if (!enabled.has_value()) {
244 LOG(ERROR) << "Failed to read feature flag - service will be disabled.";
245 Disable();
246 return;
247 }
248
249 if (enabled.value()) {
250 LOG(INFO) << "Service enabled by feature flag";
251 Enable();
252 } else {
253 LOG(INFO) << "Service disabled by feature flag";
254 Disable();
255 }
256}
257
258void Proxy::Enable() {
259 feature_enabled_ = true;
260 if (!ns_fd_.is_valid())
261 return;
262
Garrick Evansaaf9d412021-02-15 11:25:21 +0900263 SetShillProperty(patchpanel::IPv4AddressToString(ns_.peer_ipv4_address()));
Garrick Evans77e9a132021-05-13 10:51:13 +0900264 // TODO(garrick, jasongustaman): Setup routing redirection.
265}
266
267void Proxy::Disable() {
268 if (feature_enabled_ && opts_.type == Type::kSystem && ns_fd_.is_valid()) {
269 SetShillProperty("");
270 }
271 // TODO(garrick, jasongustaman): Teardown routing redirection.
272 feature_enabled_ = false;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900273}
274
Jason Jeremy Iman845f2932021-01-31 16:12:13 +0900275std::unique_ptr<Resolver> Proxy::NewResolver(base::TimeDelta timeout,
276 base::TimeDelta retry_delay,
277 int max_num_retries) {
278 return std::make_unique<Resolver>(timeout, retry_delay, max_num_retries);
Garrick Evans2ca050d2021-02-09 18:21:36 +0900279}
280
Garrick Evans34650b32021-02-03 09:24:35 +0900281void Proxy::OnDefaultDeviceChanged(const shill::Client::Device* const device) {
282 // ARC proxies will handle changes to their network in OnDeviceChanged.
283 if (opts_.type == Proxy::Type::kARC)
284 return;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900285
Garrick Evans34650b32021-02-03 09:24:35 +0900286 // Default service is either not ready yet or has just disconnected.
287 if (!device) {
288 // If it disconnected, shutdown the resolver.
289 if (device_) {
290 LOG(WARNING) << opts_
291 << " is stopping because there is no default service";
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900292 doh_config_.clear();
Garrick Evans34650b32021-02-03 09:24:35 +0900293 resolver_.reset();
294 device_.reset();
295 }
296 return;
297 }
298
Garrick Evansadde9852021-02-15 20:16:53 +0900299 shill::Client::Device new_default_device = *device;
300
Garrick Evans34650b32021-02-03 09:24:35 +0900301 // The system proxy should ignore when a VPN is turned on as it must continue
302 // to work with the underlying physical interface.
Garrick Evans34650b32021-02-03 09:24:35 +0900303 if (opts_.type == Proxy::Type::kSystem &&
Garrick Evansadde9852021-02-15 20:16:53 +0900304 device->type == shill::Client::Device::Type::kVPN) {
305 if (device_)
306 return;
307
308 // No device means that the system proxy has started up with a VPN as the
309 // default network; which means we need to dig out the physical network
310 // device and use that from here forward.
311 auto dd = shill_->DefaultDevice(true /* exclude_vpn */);
312 if (!dd) {
313 LOG(ERROR) << "No default non-VPN device found";
314 return;
315 }
316 new_default_device = *dd.get();
317 }
Garrick Evans34650b32021-02-03 09:24:35 +0900318
319 // While this is enforced in shill as well, only enable resolution if the
320 // service online.
Garrick Evansadde9852021-02-15 20:16:53 +0900321 if (new_default_device.state !=
322 shill::Client::Device::ConnectionState::kOnline) {
Garrick Evans34650b32021-02-03 09:24:35 +0900323 if (device_) {
324 LOG(WARNING) << opts_ << " is stopping because the default device ["
Garrick Evansadde9852021-02-15 20:16:53 +0900325 << new_default_device.ifname << "] is offline";
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900326 doh_config_.clear();
Garrick Evans34650b32021-02-03 09:24:35 +0900327 resolver_.reset();
328 device_.reset();
329 }
330 return;
331 }
332
333 if (!device_)
334 device_ = std::make_unique<shill::Client::Device>();
335
336 // The default network has changed.
Garrick Evansadde9852021-02-15 20:16:53 +0900337 if (new_default_device.ifname != device_->ifname)
338 LOG(INFO) << opts_ << " is now tracking [" << new_default_device.ifname
339 << "]";
Garrick Evans34650b32021-02-03 09:24:35 +0900340
Garrick Evansadde9852021-02-15 20:16:53 +0900341 *device_.get() = new_default_device;
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900342 MaybeCreateResolver();
Garrick Evansa8c12be2021-02-17 16:06:45 +0900343 UpdateNameServers(device_->ipconfig);
Garrick Evans34650b32021-02-03 09:24:35 +0900344
345 // For the system proxy, we have to tell shill about it. We should start
346 // receiving DNS traffic on success. But if this fails, we don't have much
347 // choice but to just crash out and try again.
348 if (opts_.type == Type::kSystem)
Garrick Evansaaf9d412021-02-15 11:25:21 +0900349 SetShillProperty(patchpanel::IPv4AddressToString(ns_.peer_ipv4_address()),
Garrick Evans34650b32021-02-03 09:24:35 +0900350 true /* die_on_failure */);
351}
352
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900353shill::Client::ManagerPropertyAccessor* Proxy::shill_props() {
354 if (!shill_props_) {
355 shill_props_ = shill_->ManagerProperties();
356 shill_props_->Watch(shill::kDNSProxyDOHProvidersProperty,
357 base::BindRepeating(&Proxy::OnDoHProvidersChanged,
358 weak_factory_.GetWeakPtr()));
359 }
360
361 return shill_props_.get();
362}
363
Garrick Evans34650b32021-02-03 09:24:35 +0900364void Proxy::OnDeviceChanged(const shill::Client::Device* const device) {
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900365 if (!device || (device_ && device_->ifname != device->ifname))
Garrick Evansa8c12be2021-02-17 16:06:45 +0900366 return;
367
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900368 switch (opts_.type) {
369 case Type::kDefault:
370 // We don't need to worry about this here since the default proxy
371 // always/only tracks the default device and any update will be handled by
372 // OnDefaultDeviceChanged.
373 return;
374
375 case Type::kSystem:
376 if (!device_ || device_->ipconfig == device->ipconfig)
377 return;
378
379 UpdateNameServers(device->ipconfig);
380 device_->ipconfig = device->ipconfig;
381 return;
382
383 case Type::kARC:
384 if (opts_.ifname != device->ifname)
385 return;
386
387 if (device->state != shill::Client::Device::ConnectionState::kOnline) {
388 if (device_) {
389 LOG(WARNING) << opts_ << " is stopping because the device ["
390 << device->ifname << "] is offline";
391 doh_config_.clear();
392 resolver_.reset();
393 device_.reset();
394 }
395 return;
396 }
397
398 if (!device_) {
399 device_ = std::make_unique<shill::Client::Device>();
400 }
401
402 *device_.get() = *device;
403 MaybeCreateResolver();
404 UpdateNameServers(device->ipconfig);
405 break;
406
407 default:
408 NOTREACHED();
409 }
410}
411
412void Proxy::MaybeCreateResolver() {
413 if (resolver_)
Garrick Evansa8c12be2021-02-17 16:06:45 +0900414 return;
415
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900416 resolver_ =
417 NewResolver(kRequestTimeout, kRequestRetryDelay, kRequestMaxRetry);
418 doh_config_.set_resolver(resolver_.get());
Garrick Evansa8c12be2021-02-17 16:06:45 +0900419
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900420 struct sockaddr_in addr = {0};
421 addr.sin_family = AF_INET;
422 addr.sin_port = kDefaultPort;
423 addr.sin_addr.s_addr =
424 INADDR_ANY; // Since we're running in the private namespace.
425
426 CHECK(resolver_->ListenUDP(reinterpret_cast<struct sockaddr*>(&addr)))
427 << opts_ << " failed to start UDP relay loop";
428 LOG_IF(DFATAL,
429 !resolver_->ListenTCP(reinterpret_cast<struct sockaddr*>(&addr)))
430 << opts_ << " failed to start TCP relay loop";
431
432 // Fetch the DoH settings.
433 brillo::ErrorPtr error;
434 brillo::VariantDictionary doh_providers;
435 if (shill_props()->Get(shill::kDNSProxyDOHProvidersProperty, &doh_providers,
436 &error))
437 OnDoHProvidersChanged(brillo::Any(doh_providers));
438 else
439 LOG(ERROR) << opts_ << " failed to obtain DoH configuration from shill: "
440 << error->GetMessage();
Garrick Evansa8c12be2021-02-17 16:06:45 +0900441}
442
443void Proxy::UpdateNameServers(const shill::Client::IPConfig& ipconfig) {
444 auto name_servers = ipconfig.ipv4_dns_addresses;
445 // Shill sometimes adds 0.0.0.0 for some reason - so strip any if so.
446 name_servers.erase(
447 std::remove_if(name_servers.begin(), name_servers.end(),
448 [](const std::string& s) { return s == kIfAddrAny; }),
449 name_servers.end());
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900450 name_servers.insert(name_servers.end(), ipconfig.ipv6_dns_addresses.begin(),
451 ipconfig.ipv6_dns_addresses.end());
452 doh_config_.set_nameservers(name_servers);
Garrick Evansa8c12be2021-02-17 16:06:45 +0900453 LOG(INFO) << opts_ << " applied device DNS configuration";
Garrick Evans34650b32021-02-03 09:24:35 +0900454}
Garrick Evans066dc2c2020-12-10 10:43:55 +0900455
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900456void Proxy::OnDoHProvidersChanged(const brillo::Any& value) {
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900457 doh_config_.set_providers(value.Get<brillo::VariantDictionary>());
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900458}
459
Garrick Evans9c7afb82021-01-29 22:38:03 +0900460void Proxy::SetShillProperty(const std::string& addr,
461 bool die_on_failure,
462 uint8_t num_retries) {
463 if (opts_.type != Type::kSystem) {
464 LOG(DFATAL) << "Must be called from system proxy only";
465 return;
466 }
Garrick Evans48c84ef2021-01-28 11:29:42 +0900467
Garrick Evans77e9a132021-05-13 10:51:13 +0900468 // When disabled, block any attempt to set this property in shill which will
469 // cause system DNS to start to flow in.
470 if (!feature_enabled_)
471 return;
472
Garrick Evans9c7afb82021-01-29 22:38:03 +0900473 if (num_retries == 0) {
474 LOG(ERROR) << "Maximum number of retries exceeding attempt to"
475 << " set dns-proxy address property on shill";
476 CHECK(!die_on_failure);
477 return;
478 }
479
480 // This can only happen if called from OnShutdown and Setup had somehow failed
481 // to create the client... it's unlikely but regardless, that shill client
482 // isn't coming back so there's no point in retrying anything.
Garrick Evans48c84ef2021-01-28 11:29:42 +0900483 if (!shill_) {
Garrick Evans9c7afb82021-01-29 22:38:03 +0900484 LOG(ERROR)
485 << "No connection to shill - cannot set dns-proxy address property ["
486 << addr << "].";
487 return;
Garrick Evans48c84ef2021-01-28 11:29:42 +0900488 }
489
490 brillo::ErrorPtr error;
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900491 if (shill_props()->Set(shill::kDNSProxyIPv4AddressProperty, addr, &error))
Garrick Evans9c7afb82021-01-29 22:38:03 +0900492 return;
493
494 LOG(ERROR) << "Failed to set dns-proxy address property [" << addr
495 << "] on shill: " << error->GetMessage() << ". Retrying...";
496
497 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
498 FROM_HERE,
499 base::Bind(&Proxy::SetShillProperty, weak_factory_.GetWeakPtr(), addr,
500 die_on_failure, num_retries - 1),
501 kShillPropertyAttemptDelay);
Garrick Evans48c84ef2021-01-28 11:29:42 +0900502}
503
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900504void Proxy::DoHConfig::set_resolver(Resolver* resolver) {
505 resolver_ = resolver;
506 update();
507}
508
509void Proxy::DoHConfig::set_nameservers(
510 const std::vector<std::string>& nameservers) {
511 nameservers_ = nameservers;
512 update();
513}
514
515void Proxy::DoHConfig::set_providers(
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900516 const brillo::VariantDictionary& providers) {
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900517 secure_providers_.clear();
518 auto_providers_.clear();
519
520 if (providers.empty()) {
521 LOG(INFO) << "DoH: off";
522 update();
523 return;
524 }
525
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900526 for (const auto& [endpoint, value] : providers) {
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900527 // We expect that in secure, always-on to find one (or more) endpoints with
528 // no nameservers.
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900529 const auto nameservers = value.TryGet<std::string>("");
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900530 if (nameservers.empty()) {
Garrick Evans4e1fc312021-05-10 14:47:31 +0900531 secure_providers_.insert(TrimParamTemplate(endpoint));
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900532 continue;
533 }
534
535 // Remap nameserver -> secure endpoint so we can quickly determine if DoH
536 // should be attempted when the name servers change.
537 for (const auto& ns :
538 base::SplitString(nameservers, ",", base::TRIM_WHITESPACE,
539 base::SPLIT_WANT_NONEMPTY)) {
Garrick Evans4e1fc312021-05-10 14:47:31 +0900540 auto_providers_[ns] = TrimParamTemplate(endpoint);
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900541 }
542 }
543
544 // If for some reason, both collections are non-empty, prefer the automatic
545 // upgrade configuration.
546 if (!auto_providers_.empty()) {
547 secure_providers_.clear();
548 LOG(INFO) << "DoH: automatic";
549 }
550 if (!secure_providers_.empty()) {
551 LOG(INFO) << "DoH: always-on";
552 }
553 update();
554}
555
556void Proxy::DoHConfig::update() {
557 if (!resolver_)
558 return;
559
560 resolver_->SetNameServers(nameservers_);
561
562 std::set<std::string> doh_providers;
563 bool doh_always_on = false;
564 if (!secure_providers_.empty()) {
565 doh_providers = secure_providers_;
566 doh_always_on = true;
567 } else if (!auto_providers_.empty()) {
568 for (const auto& ns : nameservers_) {
569 const auto it = auto_providers_.find(ns);
570 if (it != auto_providers_.end()) {
571 doh_providers.emplace(it->second);
572 }
573 }
574 }
575
576 resolver_->SetDoHProviders(
577 std::vector(doh_providers.begin(), doh_providers.end()), doh_always_on);
578}
579
580void Proxy::DoHConfig::clear() {
581 resolver_ = nullptr;
582 secure_providers_.clear();
583 auto_providers_.clear();
584}
585
Garrick Evans066dc2c2020-12-10 10:43:55 +0900586} // namespace dns_proxy