blob: 3c862b865127187236be8d5168125d90dd819992 [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 Evans5fe2a4f2021-02-03 17:04:48 +090090Proxy::Proxy(const Options& opts,
91 std::unique_ptr<patchpanel::Client> patchpanel,
92 std::unique_ptr<shill::Client> shill)
93 : opts_(opts),
94 patchpanel_(std::move(patchpanel)),
95 shill_(std::move(shill)) {}
96
Garrick Evans9c8797d2021-02-17 21:28:10 +090097Proxy::~Proxy() {
98 if (bus_)
99 bus_->ShutdownAndBlock();
100}
101
Garrick Evans066dc2c2020-12-10 10:43:55 +0900102int Proxy::OnInit() {
103 LOG(INFO) << "Starting DNS proxy " << opts_;
104
105 /// Run after Daemon::OnInit()
106 base::ThreadTaskRunnerHandle::Get()->PostTask(
107 FROM_HERE, base::Bind(&Proxy::Setup, weak_factory_.GetWeakPtr()));
108 return DBusDaemon::OnInit();
109}
110
111void Proxy::OnShutdown(int*) {
112 LOG(INFO) << "Stopping DNS proxy " << opts_;
Garrick Evans34650b32021-02-03 09:24:35 +0900113 if (opts_.type == Type::kSystem)
114 SetShillProperty("");
Garrick Evans066dc2c2020-12-10 10:43:55 +0900115}
116
117void Proxy::Setup() {
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900118 // This is only to account for the injected client for testing.
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900119 if (!patchpanel_)
120 patchpanel_ = patchpanel::Client::New();
121
Garrick Evans066dc2c2020-12-10 10:43:55 +0900122 CHECK(patchpanel_) << "Failed to initialize patchpanel client";
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900123
124 // This is only to account for the injected client for testing.
125 if (!shill_)
126 shill_.reset(new shill::Client(bus_));
127
Garrick Evans066dc2c2020-12-10 10:43:55 +0900128 patchpanel_->RegisterOnAvailableCallback(base::BindRepeating(
129 &Proxy::OnPatchpanelReady, weak_factory_.GetWeakPtr()));
Garrick Evans4f5428c2021-02-15 11:23:54 +0900130 patchpanel_->RegisterProcessChangedCallback(base::BindRepeating(
131 &Proxy::OnPatchpanelReset, weak_factory_.GetWeakPtr()));
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900132
133 shill_->RegisterOnAvailableCallback(
134 base::BindOnce(&Proxy::OnShillReady, weak_factory_.GetWeakPtr()));
Garrick Evans066dc2c2020-12-10 10:43:55 +0900135}
136
137void Proxy::OnPatchpanelReady(bool success) {
138 CHECK(success) << "Failed to connect to patchpanel";
139
140 // The default network proxy might actually be carrying Chrome, Crostini or
141 // if a VPN is on, even ARC traffic, but we attribute this as as "user"
142 // sourced.
143 patchpanel::TrafficCounter::Source traffic_source;
144 switch (opts_.type) {
145 case Type::kSystem:
146 traffic_source = patchpanel::TrafficCounter::SYSTEM;
147 break;
148 case Type::kARC:
149 traffic_source = patchpanel::TrafficCounter::ARC;
150 break;
151 default:
152 traffic_source = patchpanel::TrafficCounter::USER;
153 }
154
155 // Note that using getpid() here requires that this minijail is not creating a
156 // new PID namespace.
157 // The default proxy (only) needs to use the VPN, if applicable, the others
158 // expressly need to avoid it.
159 auto res = patchpanel_->ConnectNamespace(
160 getpid(), opts_.ifname, true /* forward_user_traffic */,
161 opts_.type == Type::kDefault /* route_on_vpn */, traffic_source);
162 CHECK(res.first.is_valid())
163 << "Failed to establish private network namespace";
164 ns_fd_ = std::move(res.first);
Garrick Evans9c7afb82021-01-29 22:38:03 +0900165 ns_ = res.second;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900166 LOG(INFO) << "Sucessfully connected private network namespace:"
Garrick Evans9c7afb82021-01-29 22:38:03 +0900167 << ns_.host_ifname() << " <--> " << ns_.peer_ifname();
Garrick Evans48c84ef2021-01-28 11:29:42 +0900168
Garrick Evans34650b32021-02-03 09:24:35 +0900169 // Now it's safe to register these handlers and respond to them.
170 shill_->RegisterDefaultDeviceChangedHandler(base::BindRepeating(
171 &Proxy::OnDefaultDeviceChanged, weak_factory_.GetWeakPtr()));
172 shill_->RegisterDeviceChangedHandler(
173 base::BindRepeating(&Proxy::OnDeviceChanged, weak_factory_.GetWeakPtr()));
174
Garrick Evans48c84ef2021-01-28 11:29:42 +0900175 if (opts_.type == Type::kSystem)
Garrick Evans34650b32021-02-03 09:24:35 +0900176 shill_->RegisterProcessChangedHandler(
177 base::BindRepeating(&Proxy::OnShillReset, weak_factory_.GetWeakPtr()));
Garrick Evans9c7afb82021-01-29 22:38:03 +0900178}
179
Garrick Evans4f5428c2021-02-15 11:23:54 +0900180void Proxy::OnPatchpanelReset(bool reset) {
181 // If patchpanel crashes, the proxy is useless since the connected virtual
182 // network is gone. So the best bet is to exit and have the controller restart
183 // us. Note if this is the system proxy, it will inform shill on shutdown.
184 LOG(ERROR) << "Patchpanel has been shutdown - restarting DNS proxy " << opts_;
185 QuitWithExitCode(EX_UNAVAILABLE);
186
187 LOG(WARNING) << "Patchpanel has been reset";
188}
189
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900190void Proxy::OnShillReady(bool success) {
191 CHECK(success) << "Failed to connect to shill";
192 shill_->Init();
193}
194
Garrick Evans9c7afb82021-01-29 22:38:03 +0900195void Proxy::OnShillReset(bool reset) {
196 if (!reset) {
197 LOG(WARNING) << "Shill has been shutdown";
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900198 // Watch for it to return.
199 shill_->RegisterOnAvailableCallback(
200 base::BindOnce(&Proxy::OnShillReady, weak_factory_.GetWeakPtr()));
Garrick Evans9c7afb82021-01-29 22:38:03 +0900201 return;
202 }
203
Garrick Evans34650b32021-02-03 09:24:35 +0900204 // Really this means shill crashed. To be safe, explicitly reset the proxy
205 // address. We don't want to crash on failure here because shill might still
206 // have this address and try to use it. This probably redundant though with us
207 // rediscovering the default device.
208 // TODO(garrick): Remove this if so.
Garrick Evans9c7afb82021-01-29 22:38:03 +0900209 LOG(WARNING) << "Shill has been reset";
Garrick Evansaaf9d412021-02-15 11:25:21 +0900210 SetShillProperty(patchpanel::IPv4AddressToString(ns_.peer_ipv4_address()));
Garrick Evans066dc2c2020-12-10 10:43:55 +0900211}
212
Jason Jeremy Iman845f2932021-01-31 16:12:13 +0900213std::unique_ptr<Resolver> Proxy::NewResolver(base::TimeDelta timeout,
214 base::TimeDelta retry_delay,
215 int max_num_retries) {
216 return std::make_unique<Resolver>(timeout, retry_delay, max_num_retries);
Garrick Evans2ca050d2021-02-09 18:21:36 +0900217}
218
Garrick Evans34650b32021-02-03 09:24:35 +0900219void Proxy::OnDefaultDeviceChanged(const shill::Client::Device* const device) {
220 // ARC proxies will handle changes to their network in OnDeviceChanged.
221 if (opts_.type == Proxy::Type::kARC)
222 return;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900223
Garrick Evans34650b32021-02-03 09:24:35 +0900224 // Default service is either not ready yet or has just disconnected.
225 if (!device) {
226 // If it disconnected, shutdown the resolver.
227 if (device_) {
228 LOG(WARNING) << opts_
229 << " is stopping because there is no default service";
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900230 doh_config_.clear();
Garrick Evans34650b32021-02-03 09:24:35 +0900231 resolver_.reset();
232 device_.reset();
233 }
234 return;
235 }
236
Garrick Evansadde9852021-02-15 20:16:53 +0900237 shill::Client::Device new_default_device = *device;
238
Garrick Evans34650b32021-02-03 09:24:35 +0900239 // The system proxy should ignore when a VPN is turned on as it must continue
240 // to work with the underlying physical interface.
Garrick Evans34650b32021-02-03 09:24:35 +0900241 if (opts_.type == Proxy::Type::kSystem &&
Garrick Evansadde9852021-02-15 20:16:53 +0900242 device->type == shill::Client::Device::Type::kVPN) {
243 if (device_)
244 return;
245
246 // No device means that the system proxy has started up with a VPN as the
247 // default network; which means we need to dig out the physical network
248 // device and use that from here forward.
249 auto dd = shill_->DefaultDevice(true /* exclude_vpn */);
250 if (!dd) {
251 LOG(ERROR) << "No default non-VPN device found";
252 return;
253 }
254 new_default_device = *dd.get();
255 }
Garrick Evans34650b32021-02-03 09:24:35 +0900256
257 // While this is enforced in shill as well, only enable resolution if the
258 // service online.
Garrick Evansadde9852021-02-15 20:16:53 +0900259 if (new_default_device.state !=
260 shill::Client::Device::ConnectionState::kOnline) {
Garrick Evans34650b32021-02-03 09:24:35 +0900261 if (device_) {
262 LOG(WARNING) << opts_ << " is stopping because the default device ["
Garrick Evansadde9852021-02-15 20:16:53 +0900263 << new_default_device.ifname << "] is offline";
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900264 doh_config_.clear();
Garrick Evans34650b32021-02-03 09:24:35 +0900265 resolver_.reset();
266 device_.reset();
267 }
268 return;
269 }
270
271 if (!device_)
272 device_ = std::make_unique<shill::Client::Device>();
273
274 // The default network has changed.
Garrick Evansadde9852021-02-15 20:16:53 +0900275 if (new_default_device.ifname != device_->ifname)
276 LOG(INFO) << opts_ << " is now tracking [" << new_default_device.ifname
277 << "]";
Garrick Evans34650b32021-02-03 09:24:35 +0900278
Garrick Evansadde9852021-02-15 20:16:53 +0900279 *device_.get() = new_default_device;
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900280 MaybeCreateResolver();
Garrick Evansa8c12be2021-02-17 16:06:45 +0900281 UpdateNameServers(device_->ipconfig);
Garrick Evans34650b32021-02-03 09:24:35 +0900282
283 // For the system proxy, we have to tell shill about it. We should start
284 // receiving DNS traffic on success. But if this fails, we don't have much
285 // choice but to just crash out and try again.
286 if (opts_.type == Type::kSystem)
Garrick Evansaaf9d412021-02-15 11:25:21 +0900287 SetShillProperty(patchpanel::IPv4AddressToString(ns_.peer_ipv4_address()),
Garrick Evans34650b32021-02-03 09:24:35 +0900288 true /* die_on_failure */);
289}
290
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900291shill::Client::ManagerPropertyAccessor* Proxy::shill_props() {
292 if (!shill_props_) {
293 shill_props_ = shill_->ManagerProperties();
294 shill_props_->Watch(shill::kDNSProxyDOHProvidersProperty,
295 base::BindRepeating(&Proxy::OnDoHProvidersChanged,
296 weak_factory_.GetWeakPtr()));
297 }
298
299 return shill_props_.get();
300}
301
Garrick Evans34650b32021-02-03 09:24:35 +0900302void Proxy::OnDeviceChanged(const shill::Client::Device* const device) {
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900303 if (!device || (device_ && device_->ifname != device->ifname))
Garrick Evansa8c12be2021-02-17 16:06:45 +0900304 return;
305
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900306 switch (opts_.type) {
307 case Type::kDefault:
308 // We don't need to worry about this here since the default proxy
309 // always/only tracks the default device and any update will be handled by
310 // OnDefaultDeviceChanged.
311 return;
312
313 case Type::kSystem:
314 if (!device_ || device_->ipconfig == device->ipconfig)
315 return;
316
317 UpdateNameServers(device->ipconfig);
318 device_->ipconfig = device->ipconfig;
319 return;
320
321 case Type::kARC:
322 if (opts_.ifname != device->ifname)
323 return;
324
325 if (device->state != shill::Client::Device::ConnectionState::kOnline) {
326 if (device_) {
327 LOG(WARNING) << opts_ << " is stopping because the device ["
328 << device->ifname << "] is offline";
329 doh_config_.clear();
330 resolver_.reset();
331 device_.reset();
332 }
333 return;
334 }
335
336 if (!device_) {
337 device_ = std::make_unique<shill::Client::Device>();
338 }
339
340 *device_.get() = *device;
341 MaybeCreateResolver();
342 UpdateNameServers(device->ipconfig);
343 break;
344
345 default:
346 NOTREACHED();
347 }
348}
349
350void Proxy::MaybeCreateResolver() {
351 if (resolver_)
Garrick Evansa8c12be2021-02-17 16:06:45 +0900352 return;
353
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900354 resolver_ =
355 NewResolver(kRequestTimeout, kRequestRetryDelay, kRequestMaxRetry);
356 doh_config_.set_resolver(resolver_.get());
Garrick Evansa8c12be2021-02-17 16:06:45 +0900357
Garrick Evans73e8e5e2021-04-27 10:16:26 +0900358 struct sockaddr_in addr = {0};
359 addr.sin_family = AF_INET;
360 addr.sin_port = kDefaultPort;
361 addr.sin_addr.s_addr =
362 INADDR_ANY; // Since we're running in the private namespace.
363
364 CHECK(resolver_->ListenUDP(reinterpret_cast<struct sockaddr*>(&addr)))
365 << opts_ << " failed to start UDP relay loop";
366 LOG_IF(DFATAL,
367 !resolver_->ListenTCP(reinterpret_cast<struct sockaddr*>(&addr)))
368 << opts_ << " failed to start TCP relay loop";
369
370 // Fetch the DoH settings.
371 brillo::ErrorPtr error;
372 brillo::VariantDictionary doh_providers;
373 if (shill_props()->Get(shill::kDNSProxyDOHProvidersProperty, &doh_providers,
374 &error))
375 OnDoHProvidersChanged(brillo::Any(doh_providers));
376 else
377 LOG(ERROR) << opts_ << " failed to obtain DoH configuration from shill: "
378 << error->GetMessage();
Garrick Evansa8c12be2021-02-17 16:06:45 +0900379}
380
381void Proxy::UpdateNameServers(const shill::Client::IPConfig& ipconfig) {
382 auto name_servers = ipconfig.ipv4_dns_addresses;
383 // Shill sometimes adds 0.0.0.0 for some reason - so strip any if so.
384 name_servers.erase(
385 std::remove_if(name_servers.begin(), name_servers.end(),
386 [](const std::string& s) { return s == kIfAddrAny; }),
387 name_servers.end());
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900388 name_servers.insert(name_servers.end(), ipconfig.ipv6_dns_addresses.begin(),
389 ipconfig.ipv6_dns_addresses.end());
390 doh_config_.set_nameservers(name_servers);
Garrick Evansa8c12be2021-02-17 16:06:45 +0900391 LOG(INFO) << opts_ << " applied device DNS configuration";
Garrick Evans34650b32021-02-03 09:24:35 +0900392}
Garrick Evans066dc2c2020-12-10 10:43:55 +0900393
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900394void Proxy::OnDoHProvidersChanged(const brillo::Any& value) {
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900395 doh_config_.set_providers(value.Get<brillo::VariantDictionary>());
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900396}
397
Garrick Evans9c7afb82021-01-29 22:38:03 +0900398void Proxy::SetShillProperty(const std::string& addr,
399 bool die_on_failure,
400 uint8_t num_retries) {
401 if (opts_.type != Type::kSystem) {
402 LOG(DFATAL) << "Must be called from system proxy only";
403 return;
404 }
Garrick Evans48c84ef2021-01-28 11:29:42 +0900405
Garrick Evans9c7afb82021-01-29 22:38:03 +0900406 if (num_retries == 0) {
407 LOG(ERROR) << "Maximum number of retries exceeding attempt to"
408 << " set dns-proxy address property on shill";
409 CHECK(!die_on_failure);
410 return;
411 }
412
413 // This can only happen if called from OnShutdown and Setup had somehow failed
414 // to create the client... it's unlikely but regardless, that shill client
415 // isn't coming back so there's no point in retrying anything.
Garrick Evans48c84ef2021-01-28 11:29:42 +0900416 if (!shill_) {
Garrick Evans9c7afb82021-01-29 22:38:03 +0900417 LOG(ERROR)
418 << "No connection to shill - cannot set dns-proxy address property ["
419 << addr << "].";
420 return;
Garrick Evans48c84ef2021-01-28 11:29:42 +0900421 }
422
423 brillo::ErrorPtr error;
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900424 if (shill_props()->Set(shill::kDNSProxyIPv4AddressProperty, addr, &error))
Garrick Evans9c7afb82021-01-29 22:38:03 +0900425 return;
426
427 LOG(ERROR) << "Failed to set dns-proxy address property [" << addr
428 << "] on shill: " << error->GetMessage() << ". Retrying...";
429
430 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
431 FROM_HERE,
432 base::Bind(&Proxy::SetShillProperty, weak_factory_.GetWeakPtr(), addr,
433 die_on_failure, num_retries - 1),
434 kShillPropertyAttemptDelay);
Garrick Evans48c84ef2021-01-28 11:29:42 +0900435}
436
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900437void Proxy::DoHConfig::set_resolver(Resolver* resolver) {
438 resolver_ = resolver;
439 update();
440}
441
442void Proxy::DoHConfig::set_nameservers(
443 const std::vector<std::string>& nameservers) {
444 nameservers_ = nameservers;
445 update();
446}
447
448void Proxy::DoHConfig::set_providers(
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900449 const brillo::VariantDictionary& providers) {
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900450 secure_providers_.clear();
451 auto_providers_.clear();
452
453 if (providers.empty()) {
454 LOG(INFO) << "DoH: off";
455 update();
456 return;
457 }
458
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900459 for (const auto& [endpoint, value] : providers) {
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900460 // We expect that in secure, always-on to find one (or more) endpoints with
461 // no nameservers.
Garrick Evans9e5cd1e2021-03-11 22:07:44 +0900462 const auto nameservers = value.TryGet<std::string>("");
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900463 if (nameservers.empty()) {
Garrick Evans4e1fc312021-05-10 14:47:31 +0900464 secure_providers_.insert(TrimParamTemplate(endpoint));
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900465 continue;
466 }
467
468 // Remap nameserver -> secure endpoint so we can quickly determine if DoH
469 // should be attempted when the name servers change.
470 for (const auto& ns :
471 base::SplitString(nameservers, ",", base::TRIM_WHITESPACE,
472 base::SPLIT_WANT_NONEMPTY)) {
Garrick Evans4e1fc312021-05-10 14:47:31 +0900473 auto_providers_[ns] = TrimParamTemplate(endpoint);
Garrick Evansd41fdbf2021-03-03 09:15:48 +0900474 }
475 }
476
477 // If for some reason, both collections are non-empty, prefer the automatic
478 // upgrade configuration.
479 if (!auto_providers_.empty()) {
480 secure_providers_.clear();
481 LOG(INFO) << "DoH: automatic";
482 }
483 if (!secure_providers_.empty()) {
484 LOG(INFO) << "DoH: always-on";
485 }
486 update();
487}
488
489void Proxy::DoHConfig::update() {
490 if (!resolver_)
491 return;
492
493 resolver_->SetNameServers(nameservers_);
494
495 std::set<std::string> doh_providers;
496 bool doh_always_on = false;
497 if (!secure_providers_.empty()) {
498 doh_providers = secure_providers_;
499 doh_always_on = true;
500 } else if (!auto_providers_.empty()) {
501 for (const auto& ns : nameservers_) {
502 const auto it = auto_providers_.find(ns);
503 if (it != auto_providers_.end()) {
504 doh_providers.emplace(it->second);
505 }
506 }
507 }
508
509 resolver_->SetDoHProviders(
510 std::vector(doh_providers.begin(), doh_providers.end()), doh_always_on);
511}
512
513void Proxy::DoHConfig::clear() {
514 resolver_ = nullptr;
515 secure_providers_.clear();
516 auto_providers_.clear();
517}
518
Garrick Evans066dc2c2020-12-10 10:43:55 +0900519} // namespace dns_proxy