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