• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
11 #include "base/files/file_path.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/sequence_checker.h"
15 #include "base/time/time.h"
16 #include "base/timer/timer.h"
17 #include "net/base/net_export.h"
18 #include "net/dns/dns_config.h"
19 #include "net/dns/dns_hosts.h"
20 #include "net/dns/serial_worker.h"
21 #include "third_party/abseil-cpp/absl/types/optional.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     void OnConfigChangedDelayed(bool success);
101 
102     // Back pointer. `this` is expected to be owned by `service_`, making this
103     // raw pointer safe.
104     const raw_ptr<DnsConfigService> service_;
105 
106     SEQUENCE_CHECKER(sequence_checker_);
107   };
108 
109   // Reader of HOSTS files. In this base implementation, uses standard logic
110   // appropriate to most platforms to read the HOSTS file located at
111   // `hosts_file_path`.
112   class NET_EXPORT_PRIVATE HostsReader : public SerialWorker {
113    public:
114     // `service` is expected to own the created reader and thus stay valid for
115     // the lifetime of the created reader.
116     HostsReader(base::FilePath::StringPieceType hosts_file_path,
117                 DnsConfigService& service);
118     ~HostsReader() override;
119 
120     HostsReader(const HostsReader&) = delete;
121     HostsReader& operator=(const HostsReader&) = delete;
122 
123    protected:
124     class NET_EXPORT_PRIVATE WorkItem : public SerialWorker::WorkItem {
125      public:
126       explicit WorkItem(std::unique_ptr<DnsHostsParser> dns_hosts_parser);
127       ~WorkItem() override;
128 
129       // Override if needed to implement platform-specific behavior, e.g. for a
130       // platform-specific HOSTS format.
131       virtual absl::optional<DnsHosts> ReadHosts();
132 
133       // Adds any necessary additional entries to the given `DnsHosts`. Returns
134       // false on failure.
135       //
136       // Override if needed to implement platform-specific behavior.
137       virtual bool AddAdditionalHostsTo(DnsHosts& in_out_dns_hosts);
138 
139       // SerialWorker::WorkItem:
140       void DoWork() final;
141 
142      private:
143       friend HostsReader;
144 
145       absl::optional<DnsHosts> hosts_;
146       std::unique_ptr<DnsHostsParser> dns_hosts_parser_;
147     };
148 
149     // SerialWorker:
150     std::unique_ptr<SerialWorker::WorkItem> CreateWorkItem() override;
151     bool OnWorkFinished(
152         std::unique_ptr<SerialWorker::WorkItem> work_item) final;
153 
154    private:
155     // Raw pointer to owning DnsConfigService. This must never be accessed
156     // inside DoWork(), since service may be destroyed while SerialWorker is
157     // running on worker thread.
158     const raw_ptr<DnsConfigService> service_;
159 
160     const base::FilePath hosts_file_path_;
161   };
162 
163   // On detecting config change, will post and wait `config_change_delay` before
164   // triggering refreshes. Will trigger refreshes synchronously on nullopt.
165   // Useful for platforms where multiple changes may be made and detected before
166   // the config is stabilized and ready to be read.
167   explicit DnsConfigService(base::FilePath::StringPieceType hosts_file_path,
168                             absl::optional<base::TimeDelta>
169                                 config_change_delay = base::Milliseconds(50));
170 
171   // Immediately attempts to read the current configuration.
172   virtual void ReadConfigNow() = 0;
173   virtual void ReadHostsNow();
174   // Registers system watchers. Returns true iff succeeds.
175   virtual bool StartWatching() = 0;
176 
177   // Called when the current config (except hosts) has changed.
178   void InvalidateConfig();
179   // Called when the current hosts have changed.
180   void InvalidateHosts();
181 
182   // Called with new config. |config|.hosts is ignored.
183   void OnConfigRead(DnsConfig config);
184   // Called with new hosts. Rest of the config is assumed unchanged.
185   void OnHostsRead(DnsHosts hosts);
186 
187   SEQUENCE_CHECKER(sequence_checker_);
188 
189  private:
190   // The timer counts from the last Invalidate* until complete config is read.
191   void StartTimer();
192   void OnTimeout();
193   // Called when the config becomes complete. Stops the timer.
194   void OnCompleteConfig();
195 
196   // Hooks for Watcher change notifications. `succeeded` false to indicate that
197   // there was an error watching for the change.
198   void OnConfigChanged(bool succeeded);
199   void OnHostsChanged(bool succeeded);
200   void OnConfigChangedDelayed(bool succeeded);
201 
202   CallbackType callback_;
203 
204   DnsConfig dns_config_;
205 
206   // True if any of the necessary watchers failed. In that case, the service
207   // will communicate changes via OnTimeout, but will only send empty DnsConfig.
208   bool watch_failed_ = false;
209   // True after On*Read, before Invalidate*. Tells if the config is complete.
210   bool have_config_ = false;
211   bool have_hosts_ = false;
212   // True if receiver needs to be updated when the config becomes complete.
213   bool need_update_ = false;
214   // True if the last config sent was empty (instead of |dns_config_|).
215   // Set when |timer_| expires.
216   bool last_sent_empty_ = true;
217 
218   const absl::optional<base::TimeDelta> config_change_delay_;
219   const base::FilePath hosts_file_path_;
220 
221   // Created only if needed in ReadHostsNow() to avoid creating unnecessarily if
222   // overridden for a platform-specific implementation.
223   std::unique_ptr<HostsReader> hosts_reader_;
224 
225   // Started in Invalidate*, cleared in On*Read.
226   base::OneShotTimer timer_;
227 
228   base::WeakPtrFactory<DnsConfigService> weak_factory_{this};
229 };
230 
231 }  // namespace net
232 
233 #endif  // NET_DNS_DNS_CONFIG_SERVICE_H_
234