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