blob: 5273da51902772fbb1232b373f95e3bec549c450 [file] [log] [blame]
Avi Drissman64595482022-09-14 20:52:291// Copyright 2021 The Chromium Authors
Eric Orthf79bc2e2021-04-02 22:23:062// 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_linux.h"
6
Eric Orthc30a1b72021-04-07 22:32:377#include <netdb.h>
Eric Orthf79bc2e2021-04-02 22:23:068#include <netinet/in.h>
9#include <resolv.h>
10#include <sys/socket.h>
11#include <sys/types.h>
12
Eric Orth0704a01d2021-04-08 17:03:1913#include <map>
Eric Orthf79bc2e2021-04-02 22:23:0614#include <memory>
15#include <string>
16#include <type_traits>
17#include <utility>
Eric Orthc30a1b72021-04-07 22:32:3718#include <vector>
Eric Orthf79bc2e2021-04-02 22:23:0619
Eric Orthf79bc2e2021-04-02 22:23:0620#include "base/check.h"
21#include "base/files/file_path.h"
22#include "base/files/file_path_watcher.h"
Avi Drissman41c4a412023-01-11 22:45:3723#include "base/functional/bind.h"
24#include "base/functional/callback.h"
Eric Orthf79bc2e2021-04-02 22:23:0625#include "base/location.h"
26#include "base/logging.h"
Keishi Hattorif28f4f82022-06-21 11:32:1527#include "base/memory/raw_ptr.h"
Tsuyoshi Horoea5c57b2022-08-10 02:15:0128#include "base/metrics/histogram_functions.h"
Eric Ortha4b7ca02021-04-09 22:28:5829#include "base/metrics/histogram_macros.h"
Eric Orthf79bc2e2021-04-02 22:23:0630#include "base/sequence_checker.h"
31#include "base/threading/scoped_blocking_call.h"
32#include "base/time/time.h"
33#include "net/base/ip_endpoint.h"
34#include "net/dns/dns_config.h"
Eric Orthc30a1b72021-04-07 22:32:3735#include "net/dns/nsswitch_reader.h"
Lina Ismail32c5ad42021-08-14 03:59:3436#include "net/dns/public/resolv_reader.h"
Eric Orthf79bc2e2021-04-02 22:23:0637#include "net/dns/serial_worker.h"
Anton Bikineev068d2912021-05-15 20:43:5238#include "third_party/abseil-cpp/absl/types/optional.h"
Eric Orthf79bc2e2021-04-02 22:23:0639
40namespace net {
41
42namespace internal {
43
44namespace {
45
46const base::FilePath::CharType kFilePathHosts[] =
47 FILE_PATH_LITERAL("/etc/hosts");
48
49#ifndef _PATH_RESCONF // Normally defined in <resolv.h>
Eric Orthc30a1b72021-04-07 22:32:3750#define _PATH_RESCONF FILE_PATH_LITERAL("/etc/resolv.conf")
Eric Orthf79bc2e2021-04-02 22:23:0651#endif
52
Eric Orthc30a1b72021-04-07 22:32:3753constexpr base::FilePath::CharType kFilePathResolv[] = _PATH_RESCONF;
Eric Orthf79bc2e2021-04-02 22:23:0654
Eric Orthc30a1b72021-04-07 22:32:3755#ifndef _PATH_NSSWITCH_CONF // Normally defined in <netdb.h>
56#define _PATH_NSSWITCH_CONF FILE_PATH_LITERAL("/etc/nsswitch.conf")
57#endif
Eric Orthf79bc2e2021-04-02 22:23:0658
Eric Orthc30a1b72021-04-07 22:32:3759constexpr base::FilePath::CharType kFilePathNsswitch[] = _PATH_NSSWITCH_CONF;
Eric Orthf79bc2e2021-04-02 22:23:0660
Anton Bikineev068d2912021-05-15 20:43:5261absl::optional<DnsConfig> ConvertResStateToDnsConfig(
Eric Orthf79bc2e2021-04-02 22:23:0662 const struct __res_state& res) {
Lina Ismail32c5ad42021-08-14 03:59:3463 absl::optional<std::vector<net::IPEndPoint>> nameservers =
64 GetNameservers(res);
Eric Orthf79bc2e2021-04-02 22:23:0665 DnsConfig dns_config;
66 dns_config.unhandled_options = false;
67
Lina Ismail32c5ad42021-08-14 03:59:3468 if (!nameservers.has_value())
Anton Bikineev068d2912021-05-15 20:43:5269 return absl::nullopt;
Eric Orthf79bc2e2021-04-02 22:23:0670
Lina Ismail32c5ad42021-08-14 03:59:3471 // Expected to be validated by GetNameservers()
72 DCHECK(res.options & RES_INIT);
Eric Orthf79bc2e2021-04-02 22:23:0673
Lina Ismail32c5ad42021-08-14 03:59:3474 dns_config.nameservers = std::move(nameservers.value());
Eric Orthf79bc2e2021-04-02 22:23:0675 dns_config.search.clear();
76 for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) {
77 dns_config.search.emplace_back(res.dnsrch[i]);
78 }
79
80 dns_config.ndots = res.ndots;
Peter Kastinge5a38ed2021-10-02 03:06:3581 dns_config.fallback_period = base::Seconds(res.retrans);
Eric Orthf79bc2e2021-04-02 22:23:0682 dns_config.attempts = res.retry;
83#if defined(RES_ROTATE)
84 dns_config.rotate = res.options & RES_ROTATE;
85#endif
86#if !defined(RES_USE_DNSSEC)
87 // Some versions of libresolv don't have support for the DO bit. In this
88 // case, we proceed without it.
89 static const int RES_USE_DNSSEC = 0;
90#endif
91
92 // The current implementation assumes these options are set. They normally
93 // cannot be overwritten by /etc/resolv.conf
94 const unsigned kRequiredOptions = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
95 if ((res.options & kRequiredOptions) != kRequiredOptions) {
96 dns_config.unhandled_options = true;
97 return dns_config;
98 }
99
100 const unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC;
101 if (res.options & kUnhandledOptions) {
102 dns_config.unhandled_options = true;
103 return dns_config;
104 }
105
106 if (dns_config.nameservers.empty())
Anton Bikineev068d2912021-05-15 20:43:52107 return absl::nullopt;
Eric Orthf79bc2e2021-04-02 22:23:06108
109 // If any name server is 0.0.0.0, assume the configuration is invalid.
110 for (const IPEndPoint& nameserver : dns_config.nameservers) {
111 if (nameserver.address().IsZero())
Anton Bikineev068d2912021-05-15 20:43:52112 return absl::nullopt;
Eric Orthf79bc2e2021-04-02 22:23:06113 }
114 return dns_config;
115}
116
Eric Orth0704a01d2021-04-08 17:03:19117// Helper to add the effective result of `action` to `in_out_parsed_behavior`.
118// Returns false if `action` results in inconsistent behavior (setting an action
119// for a status that already has a different action).
120bool SetActionBehavior(const NsswitchReader::ServiceAction& action,
121 std::map<NsswitchReader::Status, NsswitchReader::Action>&
122 in_out_parsed_behavior) {
123 if (action.negated) {
124 for (NsswitchReader::Status status :
125 {NsswitchReader::Status::kSuccess, NsswitchReader::Status::kNotFound,
126 NsswitchReader::Status::kUnavailable,
127 NsswitchReader::Status::kTryAgain}) {
128 if (status != action.status) {
129 NsswitchReader::ServiceAction effective_action = {
130 /*negated=*/false, status, action.action};
131 if (!SetActionBehavior(effective_action, in_out_parsed_behavior))
132 return false;
133 }
134 }
135 } else {
136 if (in_out_parsed_behavior.count(action.status) >= 1 &&
137 in_out_parsed_behavior[action.status] != action.action) {
138 return false;
139 }
140 in_out_parsed_behavior[action.status] = action.action;
141 }
142
143 return true;
144}
145
146// Helper to determine if `actions` match `expected_actions`, meaning `actions`
147// contains no unknown statuses or actions and for every expectation set in
148// `expected_actions`, the expected action matches the effective result from
149// `actions`.
150bool AreActionsCompatible(
151 const std::vector<NsswitchReader::ServiceAction>& actions,
152 const std::map<NsswitchReader::Status, NsswitchReader::Action>
153 expected_actions) {
154 std::map<NsswitchReader::Status, NsswitchReader::Action> parsed_behavior;
155
156 for (const NsswitchReader::ServiceAction& action : actions) {
157 if (action.status == NsswitchReader::Status::kUnknown ||
158 action.action == NsswitchReader::Action::kUnknown) {
159 return false;
160 }
161
162 if (!SetActionBehavior(action, parsed_behavior))
163 return false;
164 }
165
166 // Default behavior if not configured.
167 if (parsed_behavior.count(NsswitchReader::Status::kSuccess) == 0)
168 parsed_behavior[NsswitchReader::Status::kSuccess] =
169 NsswitchReader::Action::kReturn;
170 if (parsed_behavior.count(NsswitchReader::Status::kNotFound) == 0)
171 parsed_behavior[NsswitchReader::Status::kNotFound] =
172 NsswitchReader::Action::kContinue;
173 if (parsed_behavior.count(NsswitchReader::Status::kUnavailable) == 0)
174 parsed_behavior[NsswitchReader::Status::kUnavailable] =
175 NsswitchReader::Action::kContinue;
176 if (parsed_behavior.count(NsswitchReader::Status::kTryAgain) == 0)
177 parsed_behavior[NsswitchReader::Status::kTryAgain] =
178 NsswitchReader::Action::kContinue;
179
180 for (const std::pair<const NsswitchReader::Status, NsswitchReader::Action>&
181 expected : expected_actions) {
182 if (parsed_behavior[expected.first] != expected.second)
183 return false;
184 }
185
186 return true;
187}
188
Eric Ortha4b7ca02021-04-09 22:28:58189// These values are emitted in metrics. Entries should not be renumbered and
190// numeric values should never be reused. (See NsswitchIncompatibleReason in
191// tools/metrics/histograms/enums.xml.)
Eric Orth0704a01d2021-04-08 17:03:19192enum class IncompatibleNsswitchReason {
193 kFilesMissing = 0,
Eric Ortha4b7ca02021-04-09 22:28:58194 kMultipleFiles = 1,
195 kBadFilesActions = 2,
196 kDnsMissing = 3,
197 kBadDnsActions = 4,
198 kBadMdnsMinimalActions = 5,
199 kBadOtherServiceActions = 6,
200 kUnknownService = 7,
201 kIncompatibleService = 8,
202 kMaxValue = kIncompatibleService
Eric Orth0704a01d2021-04-08 17:03:19203};
204
Eric Ortha4b7ca02021-04-09 22:28:58205void RecordIncompatibleNsswitchReason(
206 IncompatibleNsswitchReason reason,
Anton Bikineev068d2912021-05-15 20:43:52207 absl::optional<NsswitchReader::Service> service_token) {
Eric Ortha4b7ca02021-04-09 22:28:58208 if (service_token) {
Tsuyoshi Horoea5c57b2022-08-10 02:15:01209 base::UmaHistogramEnumeration(
210 "Net.DNS.DnsConfig.Nsswitch.IncompatibleService",
211 service_token.value());
Eric Ortha4b7ca02021-04-09 22:28:58212 }
Eric Orth0704a01d2021-04-08 17:03:19213}
214
Eric Orthc30a1b72021-04-07 22:32:37215bool IsNsswitchConfigCompatible(
216 const std::vector<NsswitchReader::ServiceSpecification>& nsswitch_hosts) {
Eric Orth0704a01d2021-04-08 17:03:19217 bool files_found = false;
218 for (const NsswitchReader::ServiceSpecification& specification :
219 nsswitch_hosts) {
220 switch (specification.service) {
221 case NsswitchReader::Service::kUnknown:
222 RecordIncompatibleNsswitchReason(
Eric Ortha4b7ca02021-04-09 22:28:58223 IncompatibleNsswitchReason::kUnknownService, specification.service);
Eric Orth0704a01d2021-04-08 17:03:19224 return false;
225
226 case NsswitchReader::Service::kFiles:
227 if (files_found) {
228 RecordIncompatibleNsswitchReason(
Eric Ortha4b7ca02021-04-09 22:28:58229 IncompatibleNsswitchReason::kMultipleFiles,
230 specification.service);
Eric Orth0704a01d2021-04-08 17:03:19231 return false;
232 }
233 files_found = true;
234 // Chrome will use the result on HOSTS hit and otherwise continue to
235 // DNS. `kFiles` entries must match that behavior to be compatible.
236 if (!AreActionsCompatible(specification.actions,
237 {{NsswitchReader::Status::kSuccess,
238 NsswitchReader::Action::kReturn},
239 {NsswitchReader::Status::kNotFound,
240 NsswitchReader::Action::kContinue},
241 {NsswitchReader::Status::kUnavailable,
242 NsswitchReader::Action::kContinue},
243 {NsswitchReader::Status::kTryAgain,
244 NsswitchReader::Action::kContinue}})) {
245 RecordIncompatibleNsswitchReason(
Eric Ortha4b7ca02021-04-09 22:28:58246 IncompatibleNsswitchReason::kBadFilesActions,
247 specification.service);
Eric Orth0704a01d2021-04-08 17:03:19248 return false;
249 }
250 break;
251
252 case NsswitchReader::Service::kDns:
253 if (!files_found) {
254 RecordIncompatibleNsswitchReason(
Eric Ortha4b7ca02021-04-09 22:28:58255 IncompatibleNsswitchReason::kFilesMissing,
Anton Bikineev068d2912021-05-15 20:43:52256 /*service_token=*/absl::nullopt);
Eric Orth0704a01d2021-04-08 17:03:19257 return false;
258 }
259 // Chrome will always stop if DNS finds a result or will otherwise
260 // fallback to the system resolver (and get whatever behavior is
261 // configured in nsswitch.conf), so the only compatibility requirement
262 // is that `kDns` entries are configured to return on success.
263 if (!AreActionsCompatible(specification.actions,
264 {{NsswitchReader::Status::kSuccess,
265 NsswitchReader::Action::kReturn}})) {
266 RecordIncompatibleNsswitchReason(
Eric Ortha4b7ca02021-04-09 22:28:58267 IncompatibleNsswitchReason::kBadDnsActions,
268 specification.service);
Eric Orth0704a01d2021-04-08 17:03:19269 return false;
270 }
271
272 // Ignore any entries after `kDns` because Chrome will fallback to the
273 // system resolver if a result was not found in DNS.
274 return true;
275
276 case NsswitchReader::Service::kMdns:
277 case NsswitchReader::Service::kMdns4:
278 case NsswitchReader::Service::kMdns6:
279 case NsswitchReader::Service::kResolve:
David Benjaminff3380c2022-10-11 16:15:32280 case NsswitchReader::Service::kNis:
Eric Orth0704a01d2021-04-08 17:03:19281 RecordIncompatibleNsswitchReason(
Eric Ortha4b7ca02021-04-09 22:28:58282 IncompatibleNsswitchReason::kIncompatibleService,
283 specification.service);
Eric Orth0704a01d2021-04-08 17:03:19284 return false;
285
286 case NsswitchReader::Service::kMdnsMinimal:
287 case NsswitchReader::Service::kMdns4Minimal:
288 case NsswitchReader::Service::kMdns6Minimal:
289 // Always compatible as long as `kUnavailable` is `kContinue` because
290 // the service is expected to always result in `kUnavailable` for any
291 // names Chrome would attempt to resolve (non-*.local names because
292 // Chrome always delegates *.local names to the system resolver).
293 if (!AreActionsCompatible(specification.actions,
294 {{NsswitchReader::Status::kUnavailable,
295 NsswitchReader::Action::kContinue}})) {
296 RecordIncompatibleNsswitchReason(
Eric Ortha4b7ca02021-04-09 22:28:58297 IncompatibleNsswitchReason::kBadMdnsMinimalActions,
298 specification.service);
Eric Orth0704a01d2021-04-08 17:03:19299 return false;
300 }
301 break;
302
303 case NsswitchReader::Service::kMyHostname:
Eric Orth0704a01d2021-04-08 17:03:19304 // Similar enough to Chrome behavior (or unlikely to matter for Chrome
305 // resolutions) to be considered compatible unless the actions do
306 // something very weird to skip remaining services without a result.
307 if (!AreActionsCompatible(specification.actions,
308 {{NsswitchReader::Status::kNotFound,
309 NsswitchReader::Action::kContinue},
310 {NsswitchReader::Status::kUnavailable,
311 NsswitchReader::Action::kContinue},
312 {NsswitchReader::Status::kTryAgain,
313 NsswitchReader::Action::kContinue}})) {
314 RecordIncompatibleNsswitchReason(
Eric Ortha4b7ca02021-04-09 22:28:58315 IncompatibleNsswitchReason::kBadOtherServiceActions,
316 specification.service);
Eric Orth0704a01d2021-04-08 17:03:19317 return false;
318 }
319 break;
320 }
321 }
322
Eric Ortha4b7ca02021-04-09 22:28:58323 RecordIncompatibleNsswitchReason(IncompatibleNsswitchReason::kDnsMissing,
Anton Bikineev068d2912021-05-15 20:43:52324 /*service_token=*/absl::nullopt);
Eric Orth0704a01d2021-04-08 17:03:19325 return false;
Eric Orthc30a1b72021-04-07 22:32:37326}
327
328} // namespace
329
330class DnsConfigServiceLinux::Watcher : public DnsConfigService::Watcher {
331 public:
332 explicit Watcher(DnsConfigServiceLinux& service)
333 : DnsConfigService::Watcher(service) {}
334 ~Watcher() override = default;
335
336 Watcher(const Watcher&) = delete;
337 Watcher& operator=(const Watcher&) = delete;
338
339 bool Watch() override {
340 CheckOnCorrectSequence();
341
342 bool success = true;
343 if (!resolv_watcher_.Watch(
344 base::FilePath(kFilePathResolv),
345 base::FilePathWatcher::Type::kNonRecursive,
Eric Ortha4b7ca02021-04-09 22:28:58346 base::BindRepeating(&Watcher::OnResolvFilePathWatcherChange,
Eric Orthc30a1b72021-04-07 22:32:37347 base::Unretained(this)))) {
348 LOG(ERROR) << "DNS config (resolv.conf) watch failed to start.";
349 success = false;
350 }
351
352 if (!nsswitch_watcher_.Watch(
353 base::FilePath(kFilePathNsswitch),
354 base::FilePathWatcher::Type::kNonRecursive,
Eric Ortha4b7ca02021-04-09 22:28:58355 base::BindRepeating(&Watcher::OnNsswitchFilePathWatcherChange,
Eric Orthc30a1b72021-04-07 22:32:37356 base::Unretained(this)))) {
357 LOG(ERROR) << "DNS nsswitch.conf watch failed to start.";
358 success = false;
359 }
360
361 if (!hosts_watcher_.Watch(
362 base::FilePath(kFilePathHosts),
363 base::FilePathWatcher::Type::kNonRecursive,
364 base::BindRepeating(&Watcher::OnHostsFilePathWatcherChange,
365 base::Unretained(this)))) {
366 LOG(ERROR) << "DNS hosts watch failed to start.";
367 success = false;
368 }
369 return success;
370 }
371
372 private:
Eric Ortha4b7ca02021-04-09 22:28:58373 void OnResolvFilePathWatcherChange(const base::FilePath& path, bool error) {
Tsuyoshi Horoea5c57b2022-08-10 02:15:01374 base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.FileChange", true);
Eric Ortha4b7ca02021-04-09 22:28:58375 OnConfigChanged(!error);
376 }
377
378 void OnNsswitchFilePathWatcherChange(const base::FilePath& path, bool error) {
Tsuyoshi Horoea5c57b2022-08-10 02:15:01379 base::UmaHistogramBoolean("Net.DNS.DnsConfig.Nsswitch.FileChange", true);
Eric Orthc30a1b72021-04-07 22:32:37380 OnConfigChanged(!error);
381 }
382
383 void OnHostsFilePathWatcherChange(const base::FilePath& path, bool error) {
384 OnHostsChanged(!error);
385 }
386
387 base::FilePathWatcher resolv_watcher_;
388 base::FilePathWatcher nsswitch_watcher_;
389 base::FilePathWatcher hosts_watcher_;
390};
391
392// A SerialWorker that uses libresolv to initialize res_state and converts
393// it to DnsConfig.
394class DnsConfigServiceLinux::ConfigReader : public SerialWorker {
395 public:
396 explicit ConfigReader(DnsConfigServiceLinux& service,
397 std::unique_ptr<ResolvReader> resolv_reader,
398 std::unique_ptr<NsswitchReader> nsswitch_reader)
399 : service_(&service),
Eric Orth8ec1b8e2021-10-11 19:33:47400 work_item_(std::make_unique<WorkItem>(std::move(resolv_reader),
401 std::move(nsswitch_reader))) {
Eric Orthc30a1b72021-04-07 22:32:37402 // Allow execution on another thread; nothing thread-specific about
403 // constructor.
404 DETACH_FROM_SEQUENCE(sequence_checker_);
Eric Orthc30a1b72021-04-07 22:32:37405 }
406
Eric Orth8ec1b8e2021-10-11 19:33:47407 ~ConfigReader() override = default;
408
Eric Orthc30a1b72021-04-07 22:32:37409 ConfigReader(const ConfigReader&) = delete;
410 ConfigReader& operator=(const ConfigReader&) = delete;
411
Eric Orth8ec1b8e2021-10-11 19:33:47412 std::unique_ptr<SerialWorker::WorkItem> CreateWorkItem() override {
413 // Reuse same `WorkItem` to allow reuse of contained reader objects.
414 DCHECK(work_item_);
415 return std::move(work_item_);
Eric Orthc30a1b72021-04-07 22:32:37416 }
417
Erik Andersond328a542022-03-04 17:35:30418 bool OnWorkFinished(std::unique_ptr<SerialWorker::WorkItem>
Eric Orth8ec1b8e2021-10-11 19:33:47419 serial_worker_work_item) override {
420 DCHECK(serial_worker_work_item);
421 DCHECK(!work_item_);
Eric Orthc30a1b72021-04-07 22:32:37422 DCHECK(!IsCancelled());
Eric Orth8ec1b8e2021-10-11 19:33:47423
424 work_item_.reset(static_cast<WorkItem*>(serial_worker_work_item.release()));
425 if (work_item_->dns_config_.has_value()) {
426 service_->OnConfigRead(std::move(work_item_->dns_config_).value());
Erik Andersond328a542022-03-04 17:35:30427 return true;
Eric Orthc30a1b72021-04-07 22:32:37428 } else {
429 LOG(WARNING) << "Failed to read DnsConfig.";
Erik Andersond328a542022-03-04 17:35:30430 return false;
Eric Orthc30a1b72021-04-07 22:32:37431 }
432 }
433
434 private:
Eric Orth8ec1b8e2021-10-11 19:33:47435 class WorkItem : public SerialWorker::WorkItem {
436 public:
437 WorkItem(std::unique_ptr<ResolvReader> resolv_reader,
438 std::unique_ptr<NsswitchReader> nsswitch_reader)
439 : resolv_reader_(std::move(resolv_reader)),
440 nsswitch_reader_(std::move(nsswitch_reader)) {
441 DCHECK(resolv_reader_);
442 DCHECK(nsswitch_reader_);
443 }
Eric Orthc30a1b72021-04-07 22:32:37444
Eric Orth8ec1b8e2021-10-11 19:33:47445 void DoWork() override {
446 base::ScopedBlockingCall scoped_blocking_call(
447 FROM_HERE, base::BlockingType::MAY_BLOCK);
448
Dominique Fauteux-Chapleaue08899a2021-12-15 21:03:40449 {
450 std::unique_ptr<ScopedResState> res = resolv_reader_->GetResState();
451 if (res) {
452 dns_config_ = ConvertResStateToDnsConfig(res->state());
453 }
Eric Orth8ec1b8e2021-10-11 19:33:47454 }
455
Tsuyoshi Horoea5c57b2022-08-10 02:15:01456 base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.Read",
457 dns_config_.has_value());
Eric Orth8ec1b8e2021-10-11 19:33:47458 if (!dns_config_.has_value())
459 return;
Tsuyoshi Horoea5c57b2022-08-10 02:15:01460 base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.Valid",
461 dns_config_->IsValid());
462 base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.Compatible",
463 !dns_config_->unhandled_options);
Eric Orth8ec1b8e2021-10-11 19:33:47464
465 // Override `fallback_period` value to match default setting on
466 // Windows.
467 dns_config_->fallback_period = kDnsDefaultFallbackPeriod;
468
469 if (dns_config_ && !dns_config_->unhandled_options) {
470 std::vector<NsswitchReader::ServiceSpecification> nsswitch_hosts =
471 nsswitch_reader_->ReadAndParseHosts();
Eric Orth8ec1b8e2021-10-11 19:33:47472 dns_config_->unhandled_options =
473 !IsNsswitchConfigCompatible(nsswitch_hosts);
Tsuyoshi Horoea5c57b2022-08-10 02:15:01474 base::UmaHistogramBoolean("Net.DNS.DnsConfig.Nsswitch.Compatible",
475 !dns_config_->unhandled_options);
Eric Orth8ec1b8e2021-10-11 19:33:47476 }
477 }
478
479 private:
480 friend class ConfigReader;
481 absl::optional<DnsConfig> dns_config_;
482 std::unique_ptr<ResolvReader> resolv_reader_;
483 std::unique_ptr<NsswitchReader> nsswitch_reader_;
484 };
485
486 // Raw pointer to owning DnsConfigService.
Keishi Hattorif28f4f82022-06-21 11:32:15487 const raw_ptr<DnsConfigServiceLinux> service_;
Eric Orth8ec1b8e2021-10-11 19:33:47488
489 // Null while the `WorkItem` is running on the `ThreadPool`.
490 std::unique_ptr<WorkItem> work_item_;
Eric Orthc30a1b72021-04-07 22:32:37491};
492
Eric Orthc30a1b72021-04-07 22:32:37493DnsConfigServiceLinux::DnsConfigServiceLinux()
494 : DnsConfigService(kFilePathHosts) {
495 // Allow constructing on one thread and living on another.
496 DETACH_FROM_SEQUENCE(sequence_checker_);
497}
498
499DnsConfigServiceLinux::~DnsConfigServiceLinux() {
500 if (config_reader_)
501 config_reader_->Cancel();
502}
503
504void DnsConfigServiceLinux::ReadConfigNow() {
505 if (!config_reader_)
506 CreateReader();
507 config_reader_->WorkNow();
508}
509
510bool DnsConfigServiceLinux::StartWatching() {
511 CreateReader();
512 watcher_ = std::make_unique<Watcher>(*this);
513 return watcher_->Watch();
514}
515
516void DnsConfigServiceLinux::CreateReader() {
517 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
518 DCHECK(!config_reader_);
519 DCHECK(resolv_reader_);
520 DCHECK(nsswitch_reader_);
Eric Orth8ec1b8e2021-10-11 19:33:47521 config_reader_ = std::make_unique<ConfigReader>(
Eric Orthc30a1b72021-04-07 22:32:37522 *this, std::move(resolv_reader_), std::move(nsswitch_reader_));
523}
524
Eric Orthf79bc2e2021-04-02 22:23:06525} // namespace internal
526
527// static
528std::unique_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
529 return std::make_unique<internal::DnsConfigServiceLinux>();
530}
531
532} // namespace net