blob: f84c5860f9b237fd36c8b56071675150bc5bb618 [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 Evans48172782021-05-14 13:54:15 +0900263 if (opts_.type == Type::kSystem)
264 SetShillProperty(patchpanel::IPv4AddressToString(ns_.peer_ipv4_address()));
265
Garrick Evans77e9a132021-05-13 10:51:13 +0900266 // TODO(garrick, jasongustaman): Setup routing redirection.
267}
268
269void Proxy::Disable() {
270 if (feature_enabled_ && opts_.type == Type::kSystem && ns_fd_.is_valid()) {
271 SetShillProperty("");
272 }
273 // TODO(garrick, jasongustaman): Teardown routing redirection.
274 feature_enabled_ = false;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900275}
276
Jason Jeremy Iman845f2932021-01-31 16:12:13 +0900277std::unique_ptr<Resolver> Proxy::NewResolver(base::TimeDelta timeout,
278 base::TimeDelta retry_delay,
279 int max_num_retries) {
280 return std::make_unique<Resolver>(timeout, retry_delay, max_num_retries);
Garrick Evans2ca050d2021-02-09 18:21:36 +0900281}
282
Garrick Evans34650b32021-02-03 09:24:35 +0900283void Proxy::OnDefaultDeviceChanged(const shill::Client::Device* const device) {
284 // ARC proxies will handle changes to their network in OnDeviceChanged.
285 if (opts_.type == Proxy::Type::kARC)
286 return;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900287
Garrick Evans34650b32021-02-03 09:24:35 +0900288 // Default service is either not ready yet or has just disconnected.
289 if (!device) {
290 // If it disconnected, shutdown the resolver.
291 if (device_) {
292 LOG(WARNING) << opts_
293 << " is stopping because there is no default service";
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900294 doh_config_.clear();
Garrick Evans34650b32021-02-03 09:24:35 +0900295 resolver_.reset();
296 device_.reset();
297 }
298 return;
299 }
300
Garrick Evansadde9852021-02-15 20:16:53 +0900301 shill::Client::Device new_default_device = *device;
302
Garrick Evans34650b32021-02-03 09:24:35 +0900303 // The system proxy should ignore when a VPN is turned on as it must continue
304 // to work with the underlying physical interface.
Garrick Evans34650b32021-02-03 09:24:35 +0900305 if (opts_.type == Proxy::Type::kSystem &&
Garrick Evansadde9852021-02-15 20:16:53 +0900306 device->type == shill::Client::Device::Type::kVPN) {
307 if (device_)
308 return;
309
310 // No device means that the system proxy has started up with a VPN as the
311 // default network; which means we need to dig out the physical network
312 // device and use that from here forward.
313 auto dd = shill_->DefaultDevice(true /* exclude_vpn */);
314 if (!dd) {
315 LOG(ERROR) << "No default non-VPN device found";
316 return;
317 }
318 new_default_device = *dd.get();
319 }
Garrick Evans34650b32021-02-03 09:24:35 +0900320
321 // While this is enforced in shill as well, only enable resolution if the
322 // service online.
Garrick Evansadde9852021-02-15 20:16:53 +0900323 if (new_default_device.state !=
324 shill::Client::Device::ConnectionState::kOnline) {
Garrick Evans34650b32021-02-03 09:24:35 +0900325 if (device_) {
326 LOG(WARNING) << opts_ << " is stopping because the default device ["
Garrick Evansadde9852021-02-15 20:16:53 +0900327 << new_default_device.ifname << "] is offline";
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900328 doh_config_.clear();
Garrick Evans34650b32021-02-03 09:24:35 +0900329 resolver_.reset();
330 device_.reset();
331 }
332 return;
333 }
334
335 if (!device_)
336 device_ = std::make_unique<shill::Client::Device>();
337
338 // The default network has changed.
Garrick Evansadde9852021-02-15 20:16:53 +0900339 if (new_default_device.ifname != device_->ifname)
340 LOG(INFO) << opts_ << " is now tracking [" << new_default_device.ifname
341 << "]";
Garrick Evans34650b32021-02-03 09:24:35 +0900342
Garrick Evansadde9852021-02-15 20:16:53 +0900343 *device_.get() = new_default_device;
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900344 MaybeCreateResolver();
Garrick Evansa8c12be2021-02-17 16:06:45 +0900345 UpdateNameServers(device_->ipconfig);
Garrick Evans34650b32021-02-03 09:24:35 +0900346
347 // For the system proxy, we have to tell shill about it. We should start
348 // receiving DNS traffic on success. But if this fails, we don't have much
349 // choice but to just crash out and try again.
350 if (opts_.type == Type::kSystem)
Garrick Evansaaf9d412021-02-15 11:25:21 +0900351 SetShillProperty(patchpanel::IPv4AddressToString(ns_.peer_ipv4_address()),
Garrick Evans34650b32021-02-03 09:24:35 +0900352 true /* die_on_failure */);
353}
354
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900355shill::Client::ManagerPropertyAccessor* Proxy::shill_props() {
356 if (!shill_props_) {
357 shill_props_ = shill_->ManagerProperties();
358 shill_props_->Watch(shill::kDNSProxyDOHProvidersProperty,
359 base::BindRepeating(&Proxy::OnDoHProvidersChanged,
360 weak_factory_.GetWeakPtr()));
361 }
362
363 return shill_props_.get();
364}
365
Garrick Evans34650b32021-02-03 09:24:35 +0900366void Proxy::OnDeviceChanged(const shill::Client::Device* const device) {
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900367 if (!device || (device_ && device_->ifname != device->ifname))
Garrick Evansa8c12be2021-02-17 16:06:45 +0900368 return;
369
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900370 switch (opts_.type) {
371 case Type::kDefault:
372 // We don't need to worry about this here since the default proxy
373 // always/only tracks the default device and any update will be handled by
374 // OnDefaultDeviceChanged.
375 return;
376
377 case Type::kSystem:
378 if (!device_ || device_->ipconfig == device->ipconfig)
379 return;
380
381 UpdateNameServers(device->ipconfig);
382 device_->ipconfig = device->ipconfig;
383 return;
384
385 case Type::kARC:
386 if (opts_.ifname != device->ifname)
387 return;
388
389 if (device->state != shill::Client::Device::ConnectionState::kOnline) {
390 if (device_) {
391 LOG(WARNING) << opts_ << " is stopping because the device ["
392 << device->ifname << "] is offline";
393 doh_config_.clear();
394 resolver_.reset();
395 device_.reset();
396 }
397 return;
398 }
399
400 if (!device_) {
401 device_ = std::make_unique<shill::Client::Device>();
402 }
403
404 *device_.get() = *device;
405 MaybeCreateResolver();
406 UpdateNameServers(device->ipconfig);
407 break;
408
409 default:
410 NOTREACHED();
411 }
412}
413
414void Proxy::MaybeCreateResolver() {
415 if (resolver_)
Garrick Evansa8c12be2021-02-17 16:06:45 +0900416 return;
417
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900418 resolver_ =
419 NewResolver(kRequestTimeout, kRequestRetryDelay, kRequestMaxRetry);
420 doh_config_.set_resolver(resolver_.get());
Garrick Evansa8c12be2021-02-17 16:06:45 +0900421
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900422 struct sockaddr_in addr = {0};
423 addr.sin_family = AF_INET;
424 addr.sin_port = kDefaultPort;
425 addr.sin_addr.s_addr =
426 INADDR_ANY; // Since we're running in the private namespace.
427
428 CHECK(resolver_->ListenUDP(reinterpret_cast<struct sockaddr*>(&addr)))
429 << opts_ << " failed to start UDP relay loop";
430 LOG_IF(DFATAL,
431 !resolver_->ListenTCP(reinterpret_cast<struct sockaddr*>(&addr)))
432 << opts_ << " failed to start TCP relay loop";
433
434 // Fetch the DoH settings.
435 brillo::ErrorPtr error;
436 brillo::VariantDictionary doh_providers;
437 if (shill_props()->Get(shill::kDNSProxyDOHProvidersProperty, &doh_providers,
438 &error))
439 OnDoHProvidersChanged(brillo::Any(doh_providers));
440 else
441 LOG(ERROR) << opts_ << " failed to obtain DoH configuration from shill: "
442 << error->GetMessage();
Garrick Evansa8c12be2021-02-17 16:06:45 +0900443}
444
445void Proxy::UpdateNameServers(const shill::Client::IPConfig& ipconfig) {
446 auto name_servers = ipconfig.ipv4_dns_addresses;
447 // Shill sometimes adds 0.0.0.0 for some reason - so strip any if so.
448 name_servers.erase(
449 std::remove_if(name_servers.begin(), name_servers.end(),
450 [](const std::string& s) { return s == kIfAddrAny; }),
451 name_servers.end());
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900452 name_servers.insert(name_servers.end(), ipconfig.ipv6_dns_addresses.begin(),
453 ipconfig.ipv6_dns_addresses.end());
454 doh_config_.set_nameservers(name_servers);
Garrick Evansa8c12be2021-02-17 16:06:45 +0900455 LOG(INFO) << opts_ << " applied device DNS configuration";
Garrick Evans34650b32021-02-03 09:24:35 +0900456}
Garrick Evans066dc2c2020-12-10 10:43:55 +0900457
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900458void Proxy::OnDoHProvidersChanged(const brillo::Any& value) {
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900459 doh_config_.set_providers(value.Get<brillo::VariantDictionary>());
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900460}
461
Garrick Evans9c7afb82021-01-29 22:38:03 +0900462void Proxy::SetShillProperty(const std::string& addr,
463 bool die_on_failure,
464 uint8_t num_retries) {
465 if (opts_.type != Type::kSystem) {
466 LOG(DFATAL) << "Must be called from system proxy only";
467 return;
468 }
Garrick Evans48c84ef2021-01-28 11:29:42 +0900469
Garrick Evans77e9a132021-05-13 10:51:13 +0900470 // When disabled, block any attempt to set this property in shill which will
471 // cause system DNS to start to flow in.
472 if (!feature_enabled_)
473 return;
474
Garrick Evans9c7afb82021-01-29 22:38:03 +0900475 if (num_retries == 0) {
476 LOG(ERROR) << "Maximum number of retries exceeding attempt to"
477 << " set dns-proxy address property on shill";
478 CHECK(!die_on_failure);
479 return;
480 }
481
482 // This can only happen if called from OnShutdown and Setup had somehow failed
483 // to create the client... it's unlikely but regardless, that shill client
484 // isn't coming back so there's no point in retrying anything.
Garrick Evans48c84ef2021-01-28 11:29:42 +0900485 if (!shill_) {
Garrick Evans9c7afb82021-01-29 22:38:03 +0900486 LOG(ERROR)
487 << "No connection to shill - cannot set dns-proxy address property ["
488 << addr << "].";
489 return;
Garrick Evans48c84ef2021-01-28 11:29:42 +0900490 }
491
492 brillo::ErrorPtr error;
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900493 if (shill_props()->Set(shill::kDNSProxyIPv4AddressProperty, addr, &error))
Garrick Evans9c7afb82021-01-29 22:38:03 +0900494 return;
495
496 LOG(ERROR) << "Failed to set dns-proxy address property [" << addr
497 << "] on shill: " << error->GetMessage() << ". Retrying...";
498
499 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
500 FROM_HERE,
501 base::Bind(&Proxy::SetShillProperty, weak_factory_.GetWeakPtr(), addr,
502 die_on_failure, num_retries - 1),
503 kShillPropertyAttemptDelay);
Garrick Evans48c84ef2021-01-28 11:29:42 +0900504}
505
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900506void Proxy::DoHConfig::set_resolver(Resolver* resolver) {
507 resolver_ = resolver;
508 update();
509}
510
511void Proxy::DoHConfig::set_nameservers(
512 const std::vector<std::string>& nameservers) {
513 nameservers_ = nameservers;
514 update();
515}
516
517void Proxy::DoHConfig::set_providers(
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900518 const brillo::VariantDictionary& providers) {
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900519 secure_providers_.clear();
520 auto_providers_.clear();
521
522 if (providers.empty()) {
523 LOG(INFO) << "DoH: off";
524 update();
525 return;
526 }
527
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900528 for (const auto& [endpoint, value] : providers) {
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900529 // We expect that in secure, always-on to find one (or more) endpoints with
530 // no nameservers.
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900531 const auto nameservers = value.TryGet<std::string>("");
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900532 if (nameservers.empty()) {
Garrick Evans4e1fc312021-05-10 14:47:31 +0900533 secure_providers_.insert(TrimParamTemplate(endpoint));
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900534 continue;
535 }
536
537 // Remap nameserver -> secure endpoint so we can quickly determine if DoH
538 // should be attempted when the name servers change.
539 for (const auto& ns :
540 base::SplitString(nameservers, ",", base::TRIM_WHITESPACE,
541 base::SPLIT_WANT_NONEMPTY)) {
Garrick Evans4e1fc312021-05-10 14:47:31 +0900542 auto_providers_[ns] = TrimParamTemplate(endpoint);
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900543 }
544 }
545
546 // If for some reason, both collections are non-empty, prefer the automatic
547 // upgrade configuration.
548 if (!auto_providers_.empty()) {
549 secure_providers_.clear();
550 LOG(INFO) << "DoH: automatic";
551 }
552 if (!secure_providers_.empty()) {
553 LOG(INFO) << "DoH: always-on";
554 }
555 update();
556}
557
558void Proxy::DoHConfig::update() {
559 if (!resolver_)
560 return;
561
562 resolver_->SetNameServers(nameservers_);
563
564 std::set<std::string> doh_providers;
565 bool doh_always_on = false;
566 if (!secure_providers_.empty()) {
567 doh_providers = secure_providers_;
568 doh_always_on = true;
569 } else if (!auto_providers_.empty()) {
570 for (const auto& ns : nameservers_) {
571 const auto it = auto_providers_.find(ns);
572 if (it != auto_providers_.end()) {
573 doh_providers.emplace(it->second);
574 }
575 }
576 }
577
578 resolver_->SetDoHProviders(
579 std::vector(doh_providers.begin(), doh_providers.end()), doh_always_on);
580}
581
582void Proxy::DoHConfig::clear() {
583 resolver_ = nullptr;
584 secure_providers_.clear();
585 auto_providers_.clear();
586}
587
Garrick Evans066dc2c2020-12-10 10:43:55 +0900588} // namespace dns_proxy