| // Copyright 2021 The Chromium Authors |
| // 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_android.h" |
| |
| #include <sys/system_properties.h> |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/android/build_info.h" |
| #include "base/bind.h" |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/sequence_checker.h" |
| #include "base/time/time.h" |
| #include "net/android/network_library.h" |
| #include "net/base/address_tracker_linux.h" |
| #include "net/base/ip_address.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/base/network_change_notifier.h" |
| #include "net/base/network_interfaces.h" |
| #include "net/dns/dns_config.h" |
| #include "net/dns/dns_config_service.h" |
| #include "net/dns/public/dns_protocol.h" |
| #include "net/dns/serial_worker.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace net { |
| namespace internal { |
| |
| namespace { |
| |
| constexpr base::FilePath::CharType kFilePathHosts[] = |
| FILE_PATH_LITERAL("/system/etc/hosts"); |
| |
| bool IsVpnPresent() { |
| NetworkInterfaceList networks; |
| if (!GetNetworkList(&networks, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) |
| return false; |
| |
| for (NetworkInterface network : networks) { |
| if (AddressTrackerLinux::IsTunnelInterfaceName(network.name.c_str())) |
| return true; |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| // static |
| constexpr base::TimeDelta DnsConfigServiceAndroid::kConfigChangeDelay; |
| |
| class DnsConfigServiceAndroid::Watcher |
| : public DnsConfigService::Watcher, |
| public NetworkChangeNotifier::NetworkChangeObserver { |
| public: |
| explicit Watcher(DnsConfigServiceAndroid& service) |
| : DnsConfigService::Watcher(service) {} |
| ~Watcher() override { |
| NetworkChangeNotifier::RemoveNetworkChangeObserver(this); |
| } |
| |
| Watcher(const Watcher&) = delete; |
| Watcher& operator=(const Watcher&) = delete; |
| |
| // DnsConfigService::Watcher: |
| bool Watch() override { |
| CheckOnCorrectSequence(); |
| |
| // On Android, assume DNS config may have changed on every network change. |
| NetworkChangeNotifier::AddNetworkChangeObserver(this); |
| |
| // Hosts file should never change on Android (and watching it is |
| // problematic; see http://crbug.com/600442), so don't watch it. |
| |
| return true; |
| } |
| |
| // NetworkChangeNotifier::NetworkChangeObserver: |
| void OnNetworkChanged(NetworkChangeNotifier::ConnectionType type) override { |
| if (type != NetworkChangeNotifier::CONNECTION_NONE) |
| OnConfigChanged(true); |
| } |
| }; |
| |
| class DnsConfigServiceAndroid::ConfigReader : public SerialWorker { |
| public: |
| explicit ConfigReader(DnsConfigServiceAndroid& service, |
| android::DnsServerGetter dns_server_getter) |
| : dns_server_getter_(std::move(dns_server_getter)), service_(&service) {} |
| |
| ~ConfigReader() override = default; |
| |
| ConfigReader(const ConfigReader&) = delete; |
| ConfigReader& operator=(const ConfigReader&) = delete; |
| |
| std::unique_ptr<SerialWorker::WorkItem> CreateWorkItem() override { |
| return std::make_unique<WorkItem>(dns_server_getter_); |
| } |
| |
| 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: |
| explicit WorkItem(android::DnsServerGetter dns_server_getter) |
| : dns_server_getter_(std::move(dns_server_getter)) {} |
| |
| void DoWork() override { |
| dns_config_.emplace(); |
| dns_config_->unhandled_options = false; |
| |
| if (base::android::BuildInfo::GetInstance()->sdk_int() >= |
| base::android::SDK_VERSION_MARSHMALLOW) { |
| if (!dns_server_getter_.Run( |
| &dns_config_->nameservers, &dns_config_->dns_over_tls_active, |
| &dns_config_->dns_over_tls_hostname, &dns_config_->search)) { |
| dns_config_.reset(); |
| } |
| return; |
| } |
| |
| if (IsVpnPresent()) { |
| dns_config_->unhandled_options = true; |
| } |
| |
| // NOTE(pauljensen): __system_property_get and the net.dns1/2 properties |
| // are not supported APIs, but they're only read on pre-Marshmallow |
| // Android which was released years ago and isn't changing. |
| char property_value[PROP_VALUE_MAX]; |
| __system_property_get("net.dns1", property_value); |
| std::string dns1_string = property_value; |
| __system_property_get("net.dns2", property_value); |
| std::string dns2_string = property_value; |
| if (dns1_string.empty() && dns2_string.empty()) { |
| dns_config_.reset(); |
| return; |
| } |
| |
| IPAddress dns1_address; |
| IPAddress dns2_address; |
| bool parsed1 = dns1_address.AssignFromIPLiteral(dns1_string); |
| bool parsed2 = dns2_address.AssignFromIPLiteral(dns2_string); |
| if (!parsed1 && !parsed2) { |
| dns_config_.reset(); |
| return; |
| } |
| |
| if (parsed1) { |
| IPEndPoint dns1(dns1_address, dns_protocol::kDefaultPort); |
| dns_config_->nameservers.push_back(dns1); |
| } |
| if (parsed2) { |
| IPEndPoint dns2(dns2_address, dns_protocol::kDefaultPort); |
| dns_config_->nameservers.push_back(dns2); |
| } |
| } |
| |
| private: |
| friend class ConfigReader; |
| android::DnsServerGetter dns_server_getter_; |
| absl::optional<DnsConfig> dns_config_; |
| }; |
| |
| android::DnsServerGetter dns_server_getter_; |
| |
| // Raw pointer to owning DnsConfigService. |
| const raw_ptr<DnsConfigServiceAndroid> service_; |
| }; |
| |
| DnsConfigServiceAndroid::DnsConfigServiceAndroid() |
| : DnsConfigService(kFilePathHosts, kConfigChangeDelay) { |
| // Allow constructing on one thread and living on another. |
| DETACH_FROM_SEQUENCE(sequence_checker_); |
| dns_server_getter_ = base::BindRepeating(&android::GetCurrentDnsServers); |
| } |
| |
| DnsConfigServiceAndroid::~DnsConfigServiceAndroid() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (config_reader_) |
| config_reader_->Cancel(); |
| } |
| |
| void DnsConfigServiceAndroid::ReadConfigNow() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!config_reader_) { |
| DCHECK(dns_server_getter_); |
| config_reader_ = |
| std::make_unique<ConfigReader>(*this, std::move(dns_server_getter_)); |
| } |
| config_reader_->WorkNow(); |
| } |
| |
| bool DnsConfigServiceAndroid::StartWatching() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // TODO(crbug.com/116139): re-start watcher if that makes sense. |
| watcher_ = std::make_unique<Watcher>(*this); |
| return watcher_->Watch(); |
| } |
| |
| } // namespace internal |
| |
| // static |
| std::unique_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { |
| return std::make_unique<internal::DnsConfigServiceAndroid>(); |
| } |
| |
| } // namespace net |