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