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