• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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/system_dns_config_change_notifier.h"
6 
7 #include <map>
8 #include <optional>
9 #include <utility>
10 
11 #include "base/check_op.h"
12 #include "base/functional/bind.h"
13 #include "base/location.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/not_fatal_until.h"
17 #include "base/sequence_checker.h"
18 #include "base/synchronization/lock.h"
19 #include "base/task/sequenced_task_runner.h"
20 #include "base/task/task_traits.h"
21 #include "base/task/thread_pool.h"
22 #include "net/dns/dns_config_service.h"
23 
24 namespace net {
25 
26 namespace {
27 
28 // Internal information and handling for a registered Observer. Handles
29 // posting to and DCHECKing the correct sequence for the Observer.
30 class WrappedObserver {
31  public:
WrappedObserver(SystemDnsConfigChangeNotifier::Observer * observer)32   explicit WrappedObserver(SystemDnsConfigChangeNotifier::Observer* observer)
33       : task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
34         observer_(observer) {}
35 
36   WrappedObserver(const WrappedObserver&) = delete;
37   WrappedObserver& operator=(const WrappedObserver&) = delete;
38 
~WrappedObserver()39   ~WrappedObserver() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
40 
OnNotifyThreadsafe(std::optional<DnsConfig> config)41   void OnNotifyThreadsafe(std::optional<DnsConfig> config) {
42     task_runner_->PostTask(
43         FROM_HERE,
44         base::BindOnce(&WrappedObserver::OnNotify,
45                        weak_ptr_factory_.GetWeakPtr(), std::move(config)));
46   }
47 
OnNotify(std::optional<DnsConfig> config)48   void OnNotify(std::optional<DnsConfig> config) {
49     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
50     DCHECK(!config || config.value().IsValid());
51 
52     observer_->OnSystemDnsConfigChanged(std::move(config));
53   }
54 
55  private:
56   scoped_refptr<base::SequencedTaskRunner> task_runner_;
57   const raw_ptr<SystemDnsConfigChangeNotifier::Observer> observer_;
58 
59   SEQUENCE_CHECKER(sequence_checker_);
60   base::WeakPtrFactory<WrappedObserver> weak_ptr_factory_{this};
61 };
62 
63 }  // namespace
64 
65 // Internal core to be destroyed via base::OnTaskRunnerDeleter to ensure
66 // sequence safety.
67 class SystemDnsConfigChangeNotifier::Core {
68  public:
Core(scoped_refptr<base::SequencedTaskRunner> task_runner,std::unique_ptr<DnsConfigService> dns_config_service)69   Core(scoped_refptr<base::SequencedTaskRunner> task_runner,
70        std::unique_ptr<DnsConfigService> dns_config_service)
71       : task_runner_(std::move(task_runner)) {
72     DCHECK(task_runner_);
73     DCHECK(dns_config_service);
74 
75     DETACH_FROM_SEQUENCE(sequence_checker_);
76 
77     task_runner_->PostTask(FROM_HERE,
78                            base::BindOnce(&Core::SetAndStartDnsConfigService,
79                                           weak_ptr_factory_.GetWeakPtr(),
80                                           std::move(dns_config_service)));
81   }
82 
83   Core(const Core&) = delete;
84   Core& operator=(const Core&) = delete;
85 
~Core()86   ~Core() {
87     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
88     DCHECK(wrapped_observers_.empty());
89   }
90 
AddObserver(Observer * observer)91   void AddObserver(Observer* observer) {
92     // Create wrapped observer outside locking in case construction requires
93     // complex side effects.
94     auto wrapped_observer = std::make_unique<WrappedObserver>(observer);
95 
96     {
97       base::AutoLock lock(lock_);
98 
99       if (config_) {
100         // Even though this is the same sequence as the observer, use the
101         // threadsafe OnNotify to post the notification for both lock and
102         // reentrancy safety.
103         wrapped_observer->OnNotifyThreadsafe(config_);
104       }
105 
106       DCHECK_EQ(0u, wrapped_observers_.count(observer));
107       wrapped_observers_.emplace(observer, std::move(wrapped_observer));
108     }
109   }
110 
RemoveObserver(Observer * observer)111   void RemoveObserver(Observer* observer) {
112     // Destroy wrapped observer outside locking in case destruction requires
113     // complex side effects.
114     std::unique_ptr<WrappedObserver> removed_wrapped_observer;
115 
116     {
117       base::AutoLock lock(lock_);
118       auto it = wrapped_observers_.find(observer);
119       CHECK(it != wrapped_observers_.end(), base::NotFatalUntil::M130);
120       removed_wrapped_observer = std::move(it->second);
121       wrapped_observers_.erase(it);
122     }
123   }
124 
RefreshConfig()125   void RefreshConfig() {
126     task_runner_->PostTask(FROM_HERE,
127                            base::BindOnce(&Core::TriggerRefreshConfig,
128                                           weak_ptr_factory_.GetWeakPtr()));
129   }
130 
SetDnsConfigServiceForTesting(std::unique_ptr<DnsConfigService> dns_config_service,base::OnceClosure done_cb)131   void SetDnsConfigServiceForTesting(
132       std::unique_ptr<DnsConfigService> dns_config_service,
133       base::OnceClosure done_cb) {
134     DCHECK(dns_config_service);
135     task_runner_->PostTask(FROM_HERE,
136                            base::BindOnce(&Core::SetAndStartDnsConfigService,
137                                           weak_ptr_factory_.GetWeakPtr(),
138                                           std::move(dns_config_service)));
139     if (done_cb) {
140       task_runner_->PostTaskAndReply(
141           FROM_HERE,
142           base::BindOnce(&Core::TriggerRefreshConfig,
143                          weak_ptr_factory_.GetWeakPtr()),
144           std::move(done_cb));
145     }
146   }
147 
148  private:
SetAndStartDnsConfigService(std::unique_ptr<DnsConfigService> dns_config_service)149   void SetAndStartDnsConfigService(
150       std::unique_ptr<DnsConfigService> dns_config_service) {
151     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
152 
153     dns_config_service_ = std::move(dns_config_service);
154     dns_config_service_->WatchConfig(base::BindRepeating(
155         &Core::OnConfigChanged, weak_ptr_factory_.GetWeakPtr()));
156   }
157 
OnConfigChanged(const DnsConfig & config)158   void OnConfigChanged(const DnsConfig& config) {
159     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
160     base::AutoLock lock(lock_);
161 
162     // |config_| is |std::nullopt| if most recent config was invalid (or no
163     // valid config has yet been read), so convert |config| to a similar form
164     // before comparing for change.
165     std::optional<DnsConfig> new_config;
166     if (config.IsValid())
167       new_config = config;
168 
169     if (config_ == new_config)
170       return;
171 
172     config_ = std::move(new_config);
173 
174     for (auto& wrapped_observer : wrapped_observers_) {
175       wrapped_observer.second->OnNotifyThreadsafe(config_);
176     }
177   }
178 
TriggerRefreshConfig()179   void TriggerRefreshConfig() {
180     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
181     dns_config_service_->RefreshConfig();
182   }
183 
184   // Fields that may be accessed from any sequence. Must protect access using
185   // |lock_|.
186   mutable base::Lock lock_;
187   // Only stores valid configs. |std::nullopt| if most recent config was
188   // invalid (or no valid config has yet been read).
189   std::optional<DnsConfig> config_;
190   std::map<Observer*, std::unique_ptr<WrappedObserver>> wrapped_observers_;
191 
192   // Fields valid only on |task_runner_|.
193   scoped_refptr<base::SequencedTaskRunner> task_runner_;
194   SEQUENCE_CHECKER(sequence_checker_);
195   std::unique_ptr<DnsConfigService> dns_config_service_;
196   base::WeakPtrFactory<Core> weak_ptr_factory_{this};
197 };
198 
SystemDnsConfigChangeNotifier()199 SystemDnsConfigChangeNotifier::SystemDnsConfigChangeNotifier()
200     : SystemDnsConfigChangeNotifier(
201           base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}),
202           DnsConfigService::CreateSystemService()) {}
203 
SystemDnsConfigChangeNotifier(scoped_refptr<base::SequencedTaskRunner> task_runner,std::unique_ptr<DnsConfigService> dns_config_service)204 SystemDnsConfigChangeNotifier::SystemDnsConfigChangeNotifier(
205     scoped_refptr<base::SequencedTaskRunner> task_runner,
206     std::unique_ptr<DnsConfigService> dns_config_service)
207     : core_(nullptr, base::OnTaskRunnerDeleter(task_runner)) {
208   if (dns_config_service)
209     core_.reset(new Core(task_runner, std::move(dns_config_service)));
210 }
211 
212 SystemDnsConfigChangeNotifier::~SystemDnsConfigChangeNotifier() = default;
213 
AddObserver(Observer * observer)214 void SystemDnsConfigChangeNotifier::AddObserver(Observer* observer) {
215   if (core_)
216     core_->AddObserver(observer);
217 }
218 
RemoveObserver(Observer * observer)219 void SystemDnsConfigChangeNotifier::RemoveObserver(Observer* observer) {
220   if (core_)
221     core_->RemoveObserver(observer);
222 }
223 
RefreshConfig()224 void SystemDnsConfigChangeNotifier::RefreshConfig() {
225   if (core_)
226     core_->RefreshConfig();
227 }
228 
SetDnsConfigServiceForTesting(std::unique_ptr<DnsConfigService> dns_config_service,base::OnceClosure done_cb)229 void SystemDnsConfigChangeNotifier::SetDnsConfigServiceForTesting(
230     std::unique_ptr<DnsConfigService> dns_config_service,
231     base::OnceClosure done_cb) {
232   DCHECK(core_);
233   DCHECK(dns_config_service);
234 
235   core_->SetDnsConfigServiceForTesting(  // IN-TEST
236       std::move(dns_config_service), std::move(done_cb));
237 }
238 
239 }  // namespace net
240