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