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