123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "net/dns/dns_config_service_posix.h"
- #include <memory>
- #include <string>
- #include <type_traits>
- #include <utility>
- #include "base/bind.h"
- #include "base/files/file.h"
- #include "base/files/file_path.h"
- #include "base/files/file_path_watcher.h"
- #include "base/lazy_instance.h"
- #include "base/location.h"
- #include "base/logging.h"
- #include "base/memory/raw_ptr.h"
- #include "base/sequence_checker.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/threading/scoped_blocking_call.h"
- #include "base/time/time.h"
- #include "build/build_config.h"
- #include "net/base/ip_endpoint.h"
- #include "net/dns/dns_config.h"
- #include "net/dns/dns_hosts.h"
- #include "net/dns/notify_watcher_mac.h"
- #include "net/dns/public/resolv_reader.h"
- #include "net/dns/serial_worker.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- #if BUILDFLAG(IS_MAC)
- #include "net/dns/dns_config_watcher_mac.h"
- #endif
- namespace net {
- namespace internal {
- namespace {
- const base::FilePath::CharType kFilePathHosts[] =
- FILE_PATH_LITERAL("/etc/hosts");
- #if BUILDFLAG(IS_IOS)
- // There is no public API to watch the DNS configuration on iOS.
- class DnsConfigWatcher {
- public:
- using CallbackType = base::RepeatingCallback<void(bool succeeded)>;
- bool Watch(const CallbackType& callback) {
- return false;
- }
- };
- #elif BUILDFLAG(IS_MAC)
- // DnsConfigWatcher for OS_MAC is in dns_config_watcher_mac.{hh,cc}.
- #else // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_MAC)
- #ifndef _PATH_RESCONF // Normally defined in <resolv.h>
- #define _PATH_RESCONF "/etc/resolv.conf"
- #endif
- const base::FilePath::CharType kFilePathConfig[] =
- FILE_PATH_LITERAL(_PATH_RESCONF);
- class DnsConfigWatcher {
- public:
- using CallbackType = base::RepeatingCallback<void(bool succeeded)>;
- bool Watch(const CallbackType& callback) {
- callback_ = callback;
- return watcher_.Watch(base::FilePath(kFilePathConfig),
- base::FilePathWatcher::Type::kNonRecursive,
- base::BindRepeating(&DnsConfigWatcher::OnCallback,
- base::Unretained(this)));
- }
- private:
- void OnCallback(const base::FilePath& path, bool error) {
- callback_.Run(!error);
- }
- base::FilePathWatcher watcher_;
- CallbackType callback_;
- };
- #endif // BUILDFLAG(IS_IOS)
- absl::optional<DnsConfig> ReadDnsConfig() {
- base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
- base::BlockingType::MAY_BLOCK);
- absl::optional<DnsConfig> dns_config;
- {
- std::unique_ptr<ScopedResState> scoped_res_state =
- ResolvReader().GetResState();
- if (scoped_res_state) {
- dns_config = ConvertResStateToDnsConfig(scoped_res_state->state());
- }
- }
- if (!dns_config.has_value())
- return dns_config;
- #if BUILDFLAG(IS_MAC)
- if (!DnsConfigWatcher::CheckDnsConfig(
- dns_config->unhandled_options /* out_unhandled_options */)) {
- return absl::nullopt;
- }
- #endif // BUILDFLAG(IS_MAC)
- // Override |fallback_period| value to match default setting on Windows.
- dns_config->fallback_period = kDnsDefaultFallbackPeriod;
- return dns_config;
- }
- } // namespace
- class DnsConfigServicePosix::Watcher : public DnsConfigService::Watcher {
- public:
- explicit Watcher(DnsConfigServicePosix& service)
- : DnsConfigService::Watcher(service) {}
- Watcher(const Watcher&) = delete;
- Watcher& operator=(const Watcher&) = delete;
- ~Watcher() override = default;
- bool Watch() override {
- CheckOnCorrectSequence();
- bool success = true;
- if (!config_watcher_.Watch(base::BindRepeating(&Watcher::OnConfigChanged,
- base::Unretained(this)))) {
- LOG(ERROR) << "DNS config watch failed to start.";
- success = false;
- }
- // Hosts file should never change on iOS, so don't watch it there.
- #if !BUILDFLAG(IS_IOS)
- if (!hosts_watcher_.Watch(
- base::FilePath(kFilePathHosts),
- base::FilePathWatcher::Type::kNonRecursive,
- base::BindRepeating(&Watcher::OnHostsFilePathWatcherChange,
- base::Unretained(this)))) {
- LOG(ERROR) << "DNS hosts watch failed to start.";
- success = false;
- }
- #endif // !BUILDFLAG(IS_IOS)
- return success;
- }
- private:
- #if !BUILDFLAG(IS_IOS)
- void OnHostsFilePathWatcherChange(const base::FilePath& path, bool error) {
- OnHostsChanged(!error);
- }
- #endif // !BUILDFLAG(IS_IOS)
- DnsConfigWatcher config_watcher_;
- #if !BUILDFLAG(IS_IOS)
- base::FilePathWatcher hosts_watcher_;
- #endif // !BUILDFLAG(IS_IOS)
- };
- // A SerialWorker that uses libresolv to initialize res_state and converts
- // it to DnsConfig.
- class DnsConfigServicePosix::ConfigReader : public SerialWorker {
- public:
- explicit ConfigReader(DnsConfigServicePosix& service) : service_(&service) {
- // Allow execution on another thread; nothing thread-specific about
- // constructor.
- DETACH_FROM_SEQUENCE(sequence_checker_);
- }
- ~ConfigReader() override = default;
- ConfigReader(const ConfigReader&) = delete;
- ConfigReader& operator=(const ConfigReader&) = delete;
- std::unique_ptr<SerialWorker::WorkItem> CreateWorkItem() override {
- return std::make_unique<WorkItem>();
- }
- bool OnWorkFinished(std::unique_ptr<SerialWorker::WorkItem>
- serial_worker_work_item) override {
- DCHECK(serial_worker_work_item);
- DCHECK(!IsCancelled());
- WorkItem* work_item = static_cast<WorkItem*>(serial_worker_work_item.get());
- if (work_item->dns_config_.has_value()) {
- service_->OnConfigRead(std::move(work_item->dns_config_).value());
- return true;
- } else {
- LOG(WARNING) << "Failed to read DnsConfig.";
- return false;
- }
- }
- private:
- class WorkItem : public SerialWorker::WorkItem {
- public:
- void DoWork() override { dns_config_ = ReadDnsConfig(); }
- private:
- friend class ConfigReader;
- absl::optional<DnsConfig> dns_config_;
- };
- // Raw pointer to owning DnsConfigService.
- const raw_ptr<DnsConfigServicePosix> service_;
- };
- DnsConfigServicePosix::DnsConfigServicePosix()
- : DnsConfigService(kFilePathHosts) {
- // Allow constructing on one thread and living on another.
- DETACH_FROM_SEQUENCE(sequence_checker_);
- }
- DnsConfigServicePosix::~DnsConfigServicePosix() {
- if (config_reader_)
- config_reader_->Cancel();
- }
- void DnsConfigServicePosix::RefreshConfig() {
- InvalidateConfig();
- InvalidateHosts();
- ReadConfigNow();
- ReadHostsNow();
- }
- void DnsConfigServicePosix::ReadConfigNow() {
- if (!config_reader_)
- CreateReader();
- config_reader_->WorkNow();
- }
- bool DnsConfigServicePosix::StartWatching() {
- CreateReader();
- // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
- watcher_ = std::make_unique<Watcher>(*this);
- return watcher_->Watch();
- }
- void DnsConfigServicePosix::CreateReader() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(!config_reader_);
- config_reader_ = std::make_unique<ConfigReader>(*this);
- }
- absl::optional<DnsConfig> ConvertResStateToDnsConfig(
- const struct __res_state& res) {
- DnsConfig dns_config;
- dns_config.unhandled_options = false;
- if (!(res.options & RES_INIT))
- return absl::nullopt;
- absl::optional<std::vector<IPEndPoint>> nameservers = GetNameservers(res);
- if (!nameservers)
- return absl::nullopt;
- dns_config.nameservers = std::move(*nameservers);
- dns_config.search.clear();
- for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) {
- dns_config.search.emplace_back(res.dnsrch[i]);
- }
- dns_config.ndots = res.ndots;
- dns_config.fallback_period = base::Seconds(res.retrans);
- dns_config.attempts = res.retry;
- #if defined(RES_ROTATE)
- dns_config.rotate = res.options & RES_ROTATE;
- #endif
- #if !defined(RES_USE_DNSSEC)
- // Some versions of libresolv don't have support for the DO bit. In this
- // case, we proceed without it.
- static const int RES_USE_DNSSEC = 0;
- #endif
- // The current implementation assumes these options are set. They normally
- // cannot be overwritten by /etc/resolv.conf
- const unsigned kRequiredOptions = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
- if ((res.options & kRequiredOptions) != kRequiredOptions) {
- dns_config.unhandled_options = true;
- return dns_config;
- }
- const unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC;
- if (res.options & kUnhandledOptions) {
- dns_config.unhandled_options = true;
- return dns_config;
- }
- if (dns_config.nameservers.empty())
- return absl::nullopt;
- // If any name server is 0.0.0.0, assume the configuration is invalid.
- // TODO(szym): Measure how often this happens. http://crbug.com/125599
- for (const IPEndPoint& nameserver : dns_config.nameservers) {
- if (nameserver.address().IsZero())
- return absl::nullopt;
- }
- return dns_config;
- }
- } // namespace internal
- // static
- std::unique_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
- // DnsConfigService on iOS doesn't watch the config so its result can become
- // inaccurate at any time. Disable it to prevent promulgation of inaccurate
- // DnsConfigs.
- #if BUILDFLAG(IS_IOS)
- return nullptr;
- #else // BUILDFLAG(IS_IOS)
- return std::make_unique<internal::DnsConfigServicePosix>();
- #endif // BUILDFLAG(IS_IOS)
- }
- } // namespace net
|