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