xref: /aosp_15_r20/external/cronet/net/dns/dns_config_service.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 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 #ifndef NET_DNS_DNS_CONFIG_SERVICE_H_
6 #define NET_DNS_DNS_CONFIG_SERVICE_H_
7 
8 #include <map>
9 #include <memory>
10 #include <optional>
11 
12 #include "base/files/file_path.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/sequence_checker.h"
16 #include "base/time/time.h"
17 #include "base/timer/timer.h"
18 #include "net/base/net_export.h"
19 #include "net/dns/dns_config.h"
20 #include "net/dns/dns_hosts.h"
21 #include "net/dns/serial_worker.h"
22 #include "url/gurl.h"
23 
24 namespace net {
25 
26 // Service for reading system DNS settings, on demand or when signalled by
27 // internal watchers and NetworkChangeNotifier. This object is not thread-safe
28 // and methods may perform blocking I/O so methods must be called on a sequence
29 // that allows blocking (i.e. base::MayBlock).
30 class NET_EXPORT_PRIVATE DnsConfigService {
31  public:
32   // Callback interface for the client, called on the same thread as
33   // ReadConfig() and WatchConfig().
34   typedef base::RepeatingCallback<void(const DnsConfig& config)> CallbackType;
35 
36   // DHCP and user-induced changes are on the order of seconds, so 150ms should
37   // not add perceivable delay. On the other hand, config readers should finish
38   // within 150ms with the rare exception of I/O block or extra large HOSTS.
39   static const base::TimeDelta kInvalidationTimeout;
40 
41   // Creates the platform-specific DnsConfigService. May return |nullptr| if
42   // reading system DNS settings is not supported on the current platform.
43   static std::unique_ptr<DnsConfigService> CreateSystemService();
44 
45   DnsConfigService(const DnsConfigService&) = delete;
46   DnsConfigService& operator=(const DnsConfigService&) = delete;
47 
48   virtual ~DnsConfigService();
49 
50   // Attempts to read the configuration. Will run |callback| when succeeded.
51   // Can be called at most once.
52   void ReadConfig(const CallbackType& callback);
53 
54   // Registers systems watchers. Will attempt to read config after watch starts,
55   // but only if watchers started successfully. Will run |callback| iff config
56   // changes from last call or has to be withdrawn. Can be called at most once.
57   // Might require MessageLoopForIO.
58   void WatchConfig(const CallbackType& callback);
59 
60   // Triggers invalidation and re-read of the current configuration (followed by
61   // invocation of the callback). For use only on platforms expecting
62   // network-stack-external notifications of DNS config changes.
63   virtual void RefreshConfig();
64 
set_watch_failed_for_testing(bool watch_failed)65   void set_watch_failed_for_testing(bool watch_failed) {
66     watch_failed_ = watch_failed;
67   }
68 
69   // Simulates a watcher trigger by calling OnConfigChanged().
TriggerOnConfigChangedForTesting(bool succeeded)70   void TriggerOnConfigChangedForTesting(bool succeeded) {
71     // Directly call ...Delayed() version to skip past delay logic.
72     OnConfigChangedDelayed(succeeded);
73   }
74 
75  protected:
76   // Watcher to observe for changes to DNS config or HOSTS (via overriding
77   // `Watch()` with platform specifics) and trigger necessary refreshes on
78   // changes.
79   class NET_EXPORT_PRIVATE Watcher {
80    public:
81     // `service` is expected to own the created Watcher and thus stay valid for
82     // the lifetime of the created Watcher.
83     explicit Watcher(DnsConfigService& service);
84     virtual ~Watcher();
85 
86     Watcher(const Watcher&) = delete;
87     Watcher& operator=(const Watcher&) = delete;
88 
89     virtual bool Watch() = 0;
90 
91    protected:
92     // Hooks for detected changes. `succeeded` false to indicate that there was
93     // an error watching for the change.
94     void OnConfigChanged(bool succeeded);
95     void OnHostsChanged(bool succeeded);
96 
97     void CheckOnCorrectSequence();
98 
99    private:
100     // Back pointer. `this` is expected to be owned by `service_`, making this
101     // raw pointer safe.
102     const raw_ptr<DnsConfigService> service_;
103 
104     SEQUENCE_CHECKER(sequence_checker_);
105   };
106 
107   // Reader of HOSTS files. In this base implementation, uses standard logic
108   // appropriate to most platforms to read the HOSTS file located at
109   // `hosts_file_path`.
110   class NET_EXPORT_PRIVATE HostsReader : public SerialWorker {
111    public:
112     // `service` is expected to own the created reader and thus stay valid for
113     // the lifetime of the created reader.
114     HostsReader(base::FilePath::StringPieceType hosts_file_path,
115                 DnsConfigService& service);
116     ~HostsReader() override;
117 
118     HostsReader(const HostsReader&) = delete;
119     HostsReader& operator=(const HostsReader&) = delete;
120 
121    protected:
122     class NET_EXPORT_PRIVATE WorkItem : public SerialWorker::WorkItem {
123      public:
124       explicit WorkItem(std::unique_ptr<DnsHostsParser> dns_hosts_parser);
125       ~WorkItem() override;
126 
127       // Override if needed to implement platform-specific behavior, e.g. for a
128       // platform-specific HOSTS format.
129       virtual std::optional<DnsHosts> ReadHosts();
130 
131       // Adds any necessary additional entries to the given `DnsHosts`. Returns
132       // false on failure.
133       //
134       // Override if needed to implement platform-specific behavior.
135       virtual bool AddAdditionalHostsTo(DnsHosts& in_out_dns_hosts);
136 
137       // SerialWorker::WorkItem:
138       void DoWork() final;
139 
140      private:
141       friend HostsReader;
142 
143       std::optional<DnsHosts> hosts_;
144       std::unique_ptr<DnsHostsParser> dns_hosts_parser_;
145     };
146 
147     // SerialWorker:
148     std::unique_ptr<SerialWorker::WorkItem> CreateWorkItem() override;
149     bool OnWorkFinished(
150         std::unique_ptr<SerialWorker::WorkItem> work_item) final;
151 
152    private:
153     // Raw pointer to owning DnsConfigService. This must never be accessed
154     // inside DoWork(), since service may be destroyed while SerialWorker is
155     // running on worker thread.
156     const raw_ptr<DnsConfigService> service_;
157 
158     const base::FilePath hosts_file_path_;
159   };
160 
161   // On detecting config change, will post and wait `config_change_delay` before
162   // triggering refreshes. Will trigger refreshes synchronously on nullopt.
163   // Useful for platforms where multiple changes may be made and detected before
164   // the config is stabilized and ready to be read.
165   explicit DnsConfigService(base::FilePath::StringPieceType hosts_file_path,
166                             std::optional<base::TimeDelta> config_change_delay =
167                                 base::Milliseconds(50));
168 
169   // Immediately attempts to read the current configuration.
170   virtual void ReadConfigNow() = 0;
171   virtual void ReadHostsNow();
172   // Registers system watchers. Returns true iff succeeds.
173   virtual bool StartWatching() = 0;
174 
175   // Called when the current config (except hosts) has changed.
176   void InvalidateConfig();
177   // Called when the current hosts have changed.
178   void InvalidateHosts();
179 
180   // Called with new config. |config|.hosts is ignored.
181   void OnConfigRead(DnsConfig config);
182   // Called with new hosts. Rest of the config is assumed unchanged.
183   void OnHostsRead(DnsHosts hosts);
184 
185   // Called when config refresh is required. `succeeded` false to indicate that
186   // there was an error while watching for the change.
187   void OnConfigChanged(bool succeeded);
188 
189   SEQUENCE_CHECKER(sequence_checker_);
190 
191  private:
192   // The timer counts from the last Invalidate* until complete config is read.
193   void StartTimer();
194   void OnTimeout();
195   // Called when the config becomes complete. Stops the timer.
196   void OnCompleteConfig();
197 
198   // Hooks for Watcher change notifications. `succeeded` false to indicate that
199   // there was an error watching for the change.
200   void OnHostsChanged(bool succeeded);
201   void OnConfigChangedDelayed(bool succeeded);
202 
203   CallbackType callback_;
204 
205   DnsConfig dns_config_;
206 
207   // True if any of the necessary watchers failed. In that case, the service
208   // will communicate changes via OnTimeout, but will only send empty DnsConfig.
209   bool watch_failed_ = false;
210   // True after On*Read, before Invalidate*. Tells if the config is complete.
211   bool have_config_ = false;
212   bool have_hosts_ = false;
213   // True if receiver needs to be updated when the config becomes complete.
214   bool need_update_ = false;
215   // True if the last config sent was empty (instead of |dns_config_|).
216   // Set when |timer_| expires.
217   bool last_sent_empty_ = true;
218 
219   const std::optional<base::TimeDelta> config_change_delay_;
220   const base::FilePath hosts_file_path_;
221 
222   // Created only if needed in ReadHostsNow() to avoid creating unnecessarily if
223   // overridden for a platform-specific implementation.
224   std::unique_ptr<HostsReader> hosts_reader_;
225 
226   // Started in Invalidate*, cleared in On*Read.
227   base::OneShotTimer timer_;
228 
229   base::WeakPtrFactory<DnsConfigService> weak_factory_{this};
230 };
231 
232 }  // namespace net
233 
234 #endif  // NET_DNS_DNS_CONFIG_SERVICE_H_
235