xref: /aosp_15_r20/external/cronet/net/dns/dns_config_service.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
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 "net/dns/dns_config_service.h"
6 
7 #include <memory>
8 #include <optional>
9 #include <string>
10 
11 #include "base/check_op.h"
12 #include "base/files/file_path.h"
13 #include "base/functional/bind.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/notreached.h"
17 #include "base/sequence_checker.h"
18 #include "base/task/sequenced_task_runner.h"
19 #include "base/threading/scoped_blocking_call.h"
20 #include "base/time/time.h"
21 #include "net/dns/dns_hosts.h"
22 #include "net/dns/serial_worker.h"
23 
24 namespace net {
25 
26 // static
27 const base::TimeDelta DnsConfigService::kInvalidationTimeout =
28     base::Milliseconds(150);
29 
DnsConfigService(base::FilePath::StringPieceType hosts_file_path,std::optional<base::TimeDelta> config_change_delay)30 DnsConfigService::DnsConfigService(
31     base::FilePath::StringPieceType hosts_file_path,
32     std::optional<base::TimeDelta> config_change_delay)
33     : config_change_delay_(config_change_delay),
34       hosts_file_path_(hosts_file_path) {
35   DETACH_FROM_SEQUENCE(sequence_checker_);
36 }
37 
~DnsConfigService()38 DnsConfigService::~DnsConfigService() {
39   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
40   if (hosts_reader_)
41     hosts_reader_->Cancel();
42 }
43 
ReadConfig(const CallbackType & callback)44 void DnsConfigService::ReadConfig(const CallbackType& callback) {
45   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
46   DCHECK(!callback.is_null());
47   DCHECK(callback_.is_null());
48   callback_ = callback;
49   ReadConfigNow();
50   ReadHostsNow();
51 }
52 
WatchConfig(const CallbackType & callback)53 void DnsConfigService::WatchConfig(const CallbackType& callback) {
54   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
55   DCHECK(!callback.is_null());
56   DCHECK(callback_.is_null());
57   callback_ = callback;
58   watch_failed_ = !StartWatching();
59   ReadConfigNow();
60   ReadHostsNow();
61 }
62 
RefreshConfig()63 void DnsConfigService::RefreshConfig() {
64   // Overridden on supported platforms.
65   NOTREACHED();
66 }
67 
Watcher(DnsConfigService & service)68 DnsConfigService::Watcher::Watcher(DnsConfigService& service)
69     : service_(&service) {}
70 
~Watcher()71 DnsConfigService::Watcher::~Watcher() {
72   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
73 }
74 
OnConfigChanged(bool succeeded)75 void DnsConfigService::Watcher::OnConfigChanged(bool succeeded) {
76   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
77   service_->OnConfigChanged(succeeded);
78 }
79 
OnHostsChanged(bool succeeded)80 void DnsConfigService::Watcher::OnHostsChanged(bool succeeded) {
81   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
82   service_->OnHostsChanged(succeeded);
83 }
84 
CheckOnCorrectSequence()85 void DnsConfigService::Watcher::CheckOnCorrectSequence() {
86   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
87 }
88 
HostsReader(base::FilePath::StringPieceType hosts_file_path,DnsConfigService & service)89 DnsConfigService::HostsReader::HostsReader(
90     base::FilePath::StringPieceType hosts_file_path,
91     DnsConfigService& service)
92     : service_(&service), hosts_file_path_(hosts_file_path) {}
93 
94 DnsConfigService::HostsReader::~HostsReader() = default;
95 
WorkItem(std::unique_ptr<DnsHostsParser> dns_hosts_parser)96 DnsConfigService::HostsReader::WorkItem::WorkItem(
97     std::unique_ptr<DnsHostsParser> dns_hosts_parser)
98     : dns_hosts_parser_(std::move(dns_hosts_parser)) {
99   DCHECK(dns_hosts_parser_);
100 }
101 
102 DnsConfigService::HostsReader::WorkItem::~WorkItem() = default;
103 
ReadHosts()104 std::optional<DnsHosts> DnsConfigService::HostsReader::WorkItem::ReadHosts() {
105   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
106                                                 base::BlockingType::MAY_BLOCK);
107   DnsHosts dns_hosts;
108   if (!dns_hosts_parser_->ParseHosts(&dns_hosts))
109     return std::nullopt;
110 
111   return dns_hosts;
112 }
113 
AddAdditionalHostsTo(DnsHosts & in_out_dns_hosts)114 bool DnsConfigService::HostsReader::WorkItem::AddAdditionalHostsTo(
115     DnsHosts& in_out_dns_hosts) {
116   // Nothing to add in base implementation.
117   return true;
118 }
119 
DoWork()120 void DnsConfigService::HostsReader::WorkItem::DoWork() {
121   hosts_ = ReadHosts();
122   if (!hosts_.has_value())
123     return;
124 
125   if (!AddAdditionalHostsTo(hosts_.value()))
126     hosts_.reset();
127 }
128 
129 std::unique_ptr<SerialWorker::WorkItem>
CreateWorkItem()130 DnsConfigService::HostsReader::CreateWorkItem() {
131   return std::make_unique<WorkItem>(
132       std::make_unique<DnsHostsFileParser>(hosts_file_path_));
133 }
134 
OnWorkFinished(std::unique_ptr<SerialWorker::WorkItem> serial_worker_work_item)135 bool DnsConfigService::HostsReader::OnWorkFinished(
136     std::unique_ptr<SerialWorker::WorkItem> serial_worker_work_item) {
137   DCHECK(serial_worker_work_item);
138 
139   WorkItem* work_item = static_cast<WorkItem*>(serial_worker_work_item.get());
140   if (work_item->hosts_.has_value()) {
141     service_->OnHostsRead(std::move(work_item->hosts_).value());
142     return true;
143   } else {
144     LOG(WARNING) << "Failed to read DnsHosts.";
145     return false;
146   }
147 }
148 
ReadHostsNow()149 void DnsConfigService::ReadHostsNow() {
150   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
151 
152   if (!hosts_reader_) {
153     DCHECK(!hosts_file_path_.empty());
154     hosts_reader_ =
155         std::make_unique<HostsReader>(hosts_file_path_.value(), *this);
156   }
157   hosts_reader_->WorkNow();
158 }
159 
InvalidateConfig()160 void DnsConfigService::InvalidateConfig() {
161   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
162   if (!have_config_)
163     return;
164   have_config_ = false;
165   StartTimer();
166 }
167 
InvalidateHosts()168 void DnsConfigService::InvalidateHosts() {
169   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
170   if (!have_hosts_)
171     return;
172   have_hosts_ = false;
173   StartTimer();
174 }
175 
OnConfigRead(DnsConfig config)176 void DnsConfigService::OnConfigRead(DnsConfig config) {
177   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
178   DCHECK(config.IsValid());
179 
180   if (!config.EqualsIgnoreHosts(dns_config_)) {
181     dns_config_.CopyIgnoreHosts(config);
182     need_update_ = true;
183   }
184 
185   have_config_ = true;
186   if (have_hosts_ || watch_failed_)
187     OnCompleteConfig();
188 }
189 
OnHostsRead(DnsHosts hosts)190 void DnsConfigService::OnHostsRead(DnsHosts hosts) {
191   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
192 
193   if (hosts != dns_config_.hosts) {
194     dns_config_.hosts = std::move(hosts);
195     need_update_ = true;
196   }
197 
198   have_hosts_ = true;
199   if (have_config_ || watch_failed_)
200     OnCompleteConfig();
201 }
202 
StartTimer()203 void DnsConfigService::StartTimer() {
204   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
205   if (last_sent_empty_) {
206     DCHECK(!timer_.IsRunning());
207     return;  // No need to withdraw again.
208   }
209   timer_.Stop();
210 
211   // Give it a short timeout to come up with a valid config. Otherwise withdraw
212   // the config from the receiver. The goal is to avoid perceivable network
213   // outage (when using the wrong config) but at the same time avoid
214   // unnecessary Job aborts in HostResolverImpl. The signals come from multiple
215   // sources so it might receive multiple events during a config change.
216   timer_.Start(FROM_HERE, kInvalidationTimeout, this,
217                &DnsConfigService::OnTimeout);
218 }
219 
OnTimeout()220 void DnsConfigService::OnTimeout() {
221   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
222   DCHECK(!last_sent_empty_);
223   // Indicate that even if there is no change in On*Read, we will need to
224   // update the receiver when the config becomes complete.
225   need_update_ = true;
226   // Empty config is considered invalid.
227   last_sent_empty_ = true;
228   callback_.Run(DnsConfig());
229 }
230 
OnCompleteConfig()231 void DnsConfigService::OnCompleteConfig() {
232   timer_.AbandonAndStop();
233   if (!need_update_)
234     return;
235   need_update_ = false;
236   last_sent_empty_ = false;
237   if (watch_failed_) {
238     // If a watch failed, the config may not be accurate, so report empty.
239     callback_.Run(DnsConfig());
240   } else {
241     callback_.Run(dns_config_);
242   }
243 }
244 
OnConfigChanged(bool succeeded)245 void DnsConfigService::OnConfigChanged(bool succeeded) {
246   if (config_change_delay_) {
247     // Ignore transient flutter of config source by delaying the signal a bit.
248     base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
249         FROM_HERE,
250         base::BindOnce(&DnsConfigService::OnConfigChangedDelayed,
251                        weak_factory_.GetWeakPtr(), succeeded),
252         config_change_delay_.value());
253   } else {
254     OnConfigChangedDelayed(succeeded);
255   }
256 }
257 
OnHostsChanged(bool succeeded)258 void DnsConfigService::OnHostsChanged(bool succeeded) {
259   InvalidateHosts();
260   if (succeeded) {
261     ReadHostsNow();
262   } else {
263     LOG(ERROR) << "DNS hosts watch failed.";
264     watch_failed_ = true;
265   }
266 }
267 
OnConfigChangedDelayed(bool succeeded)268 void DnsConfigService::OnConfigChangedDelayed(bool succeeded) {
269   InvalidateConfig();
270   if (succeeded) {
271     ReadConfigNow();
272   } else {
273     LOG(ERROR) << "DNS config watch failed.";
274     watch_failed_ = true;
275   }
276 }
277 
278 }  // namespace net
279