1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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/dns_config_service_posix.h" 6 7 #include <string> 8 9 #include "base/basictypes.h" 10 #include "base/bind.h" 11 #include "base/files/file_path.h" 12 #include "base/files/file_path_watcher.h" 13 #include "base/memory/scoped_ptr.h" 14 #include "base/metrics/histogram.h" 15 #include "base/time/time.h" 16 #include "net/base/ip_endpoint.h" 17 #include "net/base/net_util.h" 18 #include "net/dns/dns_hosts.h" 19 #include "net/dns/dns_protocol.h" 20 #include "net/dns/notify_watcher_mac.h" 21 #include "net/dns/serial_worker.h" 22 23 namespace net { 24 25 #if !defined(OS_ANDROID) 26 namespace internal { 27 28 namespace { 29 30 const base::FilePath::CharType* kFilePathHosts = 31 FILE_PATH_LITERAL("/etc/hosts"); 32 33 #if defined(OS_MACOSX) 34 // From 10.7.3 configd-395.10/dnsinfo/dnsinfo.h 35 static const char* kDnsNotifyKey = 36 "com.apple.system.SystemConfiguration.dns_configuration"; 37 38 class ConfigWatcher { 39 public: 40 bool Watch(const base::Callback<void(bool succeeded)>& callback) { 41 return watcher_.Watch(kDnsNotifyKey, callback); 42 } 43 44 private: 45 NotifyWatcherMac watcher_; 46 }; 47 #else 48 49 #ifndef _PATH_RESCONF // Normally defined in <resolv.h> 50 #define _PATH_RESCONF "/etc/resolv.conf" 51 #endif 52 53 static const base::FilePath::CharType* kFilePathConfig = 54 FILE_PATH_LITERAL(_PATH_RESCONF); 55 56 class ConfigWatcher { 57 public: 58 typedef base::Callback<void(bool succeeded)> CallbackType; 59 60 bool Watch(const CallbackType& callback) { 61 callback_ = callback; 62 return watcher_.Watch(base::FilePath(kFilePathConfig), false, 63 base::Bind(&ConfigWatcher::OnCallback, 64 base::Unretained(this))); 65 } 66 67 private: 68 void OnCallback(const base::FilePath& path, bool error) { 69 callback_.Run(!error); 70 } 71 72 base::FilePathWatcher watcher_; 73 CallbackType callback_; 74 }; 75 #endif 76 77 ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) { 78 ConfigParsePosixResult result; 79 #if defined(OS_OPENBSD) 80 // Note: res_ninit in glibc always returns 0 and sets RES_INIT. 81 // res_init behaves the same way. 82 memset(&_res, 0, sizeof(_res)); 83 if (res_init() == 0) { 84 result = ConvertResStateToDnsConfig(_res, config); 85 } else { 86 result = CONFIG_PARSE_POSIX_RES_INIT_FAILED; 87 } 88 #else // all other OS_POSIX 89 struct __res_state res; 90 memset(&res, 0, sizeof(res)); 91 if (res_ninit(&res) == 0) { 92 result = ConvertResStateToDnsConfig(res, config); 93 } else { 94 result = CONFIG_PARSE_POSIX_RES_INIT_FAILED; 95 } 96 // Prefer res_ndestroy where available. 97 #if defined(OS_MACOSX) || defined(OS_FREEBSD) 98 res_ndestroy(&res); 99 #else 100 res_nclose(&res); 101 #endif 102 #endif 103 // Override timeout value to match default setting on Windows. 104 config->timeout = base::TimeDelta::FromSeconds(kDnsTimeoutSeconds); 105 return result; 106 } 107 108 } // namespace 109 110 class DnsConfigServicePosix::Watcher { 111 public: 112 explicit Watcher(DnsConfigServicePosix* service) 113 : weak_factory_(this), 114 service_(service) {} 115 ~Watcher() {} 116 117 bool Watch() { 118 bool success = true; 119 if (!config_watcher_.Watch(base::Bind(&Watcher::OnConfigChanged, 120 base::Unretained(this)))) { 121 LOG(ERROR) << "DNS config watch failed to start."; 122 success = false; 123 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", 124 DNS_CONFIG_WATCH_FAILED_TO_START_CONFIG, 125 DNS_CONFIG_WATCH_MAX); 126 } 127 if (!hosts_watcher_.Watch(base::FilePath(kFilePathHosts), false, 128 base::Bind(&Watcher::OnHostsChanged, 129 base::Unretained(this)))) { 130 LOG(ERROR) << "DNS hosts watch failed to start."; 131 success = false; 132 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", 133 DNS_CONFIG_WATCH_FAILED_TO_START_HOSTS, 134 DNS_CONFIG_WATCH_MAX); 135 } 136 return success; 137 } 138 139 private: 140 void OnConfigChanged(bool succeeded) { 141 // Ignore transient flutter of resolv.conf by delaying the signal a bit. 142 const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(50); 143 base::MessageLoop::current()->PostDelayedTask( 144 FROM_HERE, 145 base::Bind(&Watcher::OnConfigChangedDelayed, 146 weak_factory_.GetWeakPtr(), 147 succeeded), 148 kDelay); 149 } 150 void OnConfigChangedDelayed(bool succeeded) { 151 service_->OnConfigChanged(succeeded); 152 } 153 void OnHostsChanged(const base::FilePath& path, bool error) { 154 service_->OnHostsChanged(!error); 155 } 156 157 base::WeakPtrFactory<Watcher> weak_factory_; 158 DnsConfigServicePosix* service_; 159 ConfigWatcher config_watcher_; 160 base::FilePathWatcher hosts_watcher_; 161 162 DISALLOW_COPY_AND_ASSIGN(Watcher); 163 }; 164 165 // A SerialWorker that uses libresolv to initialize res_state and converts 166 // it to DnsConfig. 167 class DnsConfigServicePosix::ConfigReader : public SerialWorker { 168 public: 169 explicit ConfigReader(DnsConfigServicePosix* service) 170 : service_(service), success_(false) {} 171 172 virtual void DoWork() OVERRIDE { 173 base::TimeTicks start_time = base::TimeTicks::Now(); 174 ConfigParsePosixResult result = ReadDnsConfig(&dns_config_); 175 success_ = (result == CONFIG_PARSE_POSIX_OK); 176 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParsePosix", 177 result, CONFIG_PARSE_POSIX_MAX); 178 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_); 179 UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration", 180 base::TimeTicks::Now() - start_time); 181 } 182 183 virtual void OnWorkFinished() OVERRIDE { 184 DCHECK(!IsCancelled()); 185 if (success_) { 186 service_->OnConfigRead(dns_config_); 187 } else { 188 LOG(WARNING) << "Failed to read DnsConfig."; 189 } 190 } 191 192 private: 193 virtual ~ConfigReader() {} 194 195 DnsConfigServicePosix* service_; 196 // Written in DoWork, read in OnWorkFinished, no locking necessary. 197 DnsConfig dns_config_; 198 bool success_; 199 200 DISALLOW_COPY_AND_ASSIGN(ConfigReader); 201 }; 202 203 // A SerialWorker that reads the HOSTS file and runs Callback. 204 class DnsConfigServicePosix::HostsReader : public SerialWorker { 205 public: 206 explicit HostsReader(DnsConfigServicePosix* service) 207 : service_(service), path_(kFilePathHosts), success_(false) {} 208 209 private: 210 virtual ~HostsReader() {} 211 212 virtual void DoWork() OVERRIDE { 213 base::TimeTicks start_time = base::TimeTicks::Now(); 214 success_ = ParseHostsFile(path_, &hosts_); 215 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_); 216 UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration", 217 base::TimeTicks::Now() - start_time); 218 } 219 220 virtual void OnWorkFinished() OVERRIDE { 221 if (success_) { 222 service_->OnHostsRead(hosts_); 223 } else { 224 LOG(WARNING) << "Failed to read DnsHosts."; 225 } 226 } 227 228 DnsConfigServicePosix* service_; 229 const base::FilePath path_; 230 // Written in DoWork, read in OnWorkFinished, no locking necessary. 231 DnsHosts hosts_; 232 bool success_; 233 234 DISALLOW_COPY_AND_ASSIGN(HostsReader); 235 }; 236 237 DnsConfigServicePosix::DnsConfigServicePosix() 238 : config_reader_(new ConfigReader(this)), 239 hosts_reader_(new HostsReader(this)) {} 240 241 DnsConfigServicePosix::~DnsConfigServicePosix() { 242 config_reader_->Cancel(); 243 hosts_reader_->Cancel(); 244 } 245 246 void DnsConfigServicePosix::ReadNow() { 247 config_reader_->WorkNow(); 248 hosts_reader_->WorkNow(); 249 } 250 251 bool DnsConfigServicePosix::StartWatching() { 252 // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139 253 watcher_.reset(new Watcher(this)); 254 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED, 255 DNS_CONFIG_WATCH_MAX); 256 return watcher_->Watch(); 257 } 258 259 void DnsConfigServicePosix::OnConfigChanged(bool succeeded) { 260 InvalidateConfig(); 261 if (succeeded) { 262 config_reader_->WorkNow(); 263 } else { 264 LOG(ERROR) << "DNS config watch failed."; 265 set_watch_failed(true); 266 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", 267 DNS_CONFIG_WATCH_FAILED_CONFIG, 268 DNS_CONFIG_WATCH_MAX); 269 } 270 } 271 272 void DnsConfigServicePosix::OnHostsChanged(bool succeeded) { 273 InvalidateHosts(); 274 if (succeeded) { 275 hosts_reader_->WorkNow(); 276 } else { 277 LOG(ERROR) << "DNS hosts watch failed."; 278 set_watch_failed(true); 279 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", 280 DNS_CONFIG_WATCH_FAILED_HOSTS, 281 DNS_CONFIG_WATCH_MAX); 282 } 283 } 284 285 ConfigParsePosixResult ConvertResStateToDnsConfig(const struct __res_state& res, 286 DnsConfig* dns_config) { 287 CHECK(dns_config != NULL); 288 if (!(res.options & RES_INIT)) 289 return CONFIG_PARSE_POSIX_RES_INIT_UNSET; 290 291 dns_config->nameservers.clear(); 292 293 #if defined(OS_MACOSX) || defined(OS_FREEBSD) 294 union res_sockaddr_union addresses[MAXNS]; 295 int nscount = res_getservers(const_cast<res_state>(&res), addresses, MAXNS); 296 DCHECK_GE(nscount, 0); 297 DCHECK_LE(nscount, MAXNS); 298 for (int i = 0; i < nscount; ++i) { 299 IPEndPoint ipe; 300 if (!ipe.FromSockAddr( 301 reinterpret_cast<const struct sockaddr*>(&addresses[i]), 302 sizeof addresses[i])) { 303 return CONFIG_PARSE_POSIX_BAD_ADDRESS; 304 } 305 dns_config->nameservers.push_back(ipe); 306 } 307 #elif defined(OS_LINUX) 308 COMPILE_ASSERT(arraysize(res.nsaddr_list) >= MAXNS && 309 arraysize(res._u._ext.nsaddrs) >= MAXNS, 310 incompatible_libresolv_res_state); 311 DCHECK_LE(res.nscount, MAXNS); 312 // Initially, glibc stores IPv6 in |_ext.nsaddrs| and IPv4 in |nsaddr_list|. 313 // In res_send.c:res_nsend, it merges |nsaddr_list| into |nsaddrs|, 314 // but we have to combine the two arrays ourselves. 315 for (int i = 0; i < res.nscount; ++i) { 316 IPEndPoint ipe; 317 const struct sockaddr* addr = NULL; 318 size_t addr_len = 0; 319 if (res.nsaddr_list[i].sin_family) { // The indicator used by res_nsend. 320 addr = reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]); 321 addr_len = sizeof res.nsaddr_list[i]; 322 } else if (res._u._ext.nsaddrs[i] != NULL) { 323 addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]); 324 addr_len = sizeof *res._u._ext.nsaddrs[i]; 325 } else { 326 return CONFIG_PARSE_POSIX_BAD_EXT_STRUCT; 327 } 328 if (!ipe.FromSockAddr(addr, addr_len)) 329 return CONFIG_PARSE_POSIX_BAD_ADDRESS; 330 dns_config->nameservers.push_back(ipe); 331 } 332 #else // !(defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FREEBSD)) 333 DCHECK_LE(res.nscount, MAXNS); 334 for (int i = 0; i < res.nscount; ++i) { 335 IPEndPoint ipe; 336 if (!ipe.FromSockAddr( 337 reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]), 338 sizeof res.nsaddr_list[i])) { 339 return CONFIG_PARSE_POSIX_BAD_ADDRESS; 340 } 341 dns_config->nameservers.push_back(ipe); 342 } 343 #endif 344 345 dns_config->search.clear(); 346 for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) { 347 dns_config->search.push_back(std::string(res.dnsrch[i])); 348 } 349 350 dns_config->ndots = res.ndots; 351 dns_config->timeout = base::TimeDelta::FromSeconds(res.retrans); 352 dns_config->attempts = res.retry; 353 #if defined(RES_ROTATE) 354 dns_config->rotate = res.options & RES_ROTATE; 355 #endif 356 dns_config->edns0 = res.options & RES_USE_EDNS0; 357 358 // The current implementation assumes these options are set. They normally 359 // cannot be overwritten by /etc/resolv.conf 360 unsigned kRequiredOptions = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; 361 if ((res.options & kRequiredOptions) != kRequiredOptions) 362 return CONFIG_PARSE_POSIX_MISSING_OPTIONS; 363 364 unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC; 365 if (res.options & kUnhandledOptions) 366 return CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS; 367 368 if (dns_config->nameservers.empty()) 369 return CONFIG_PARSE_POSIX_NO_NAMESERVERS; 370 371 // If any name server is 0.0.0.0, assume the configuration is invalid. 372 // TODO(szym): Measure how often this happens. http://crbug.com/125599 373 const IPAddressNumber kEmptyAddress(kIPv4AddressSize); 374 for (unsigned i = 0; i < dns_config->nameservers.size(); ++i) { 375 if (dns_config->nameservers[i].address() == kEmptyAddress) 376 return CONFIG_PARSE_POSIX_NULL_ADDRESS; 377 } 378 return CONFIG_PARSE_POSIX_OK; 379 } 380 381 } // namespace internal 382 383 // static 384 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { 385 return scoped_ptr<DnsConfigService>(new internal::DnsConfigServicePosix()); 386 } 387 388 #else // defined(OS_ANDROID) 389 // Android NDK provides only a stub <resolv.h> header. 390 class StubDnsConfigService : public DnsConfigService { 391 public: 392 StubDnsConfigService() {} 393 virtual ~StubDnsConfigService() {} 394 private: 395 virtual void ReadNow() OVERRIDE {} 396 virtual bool StartWatching() OVERRIDE { return false; } 397 }; 398 // static 399 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { 400 return scoped_ptr<DnsConfigService>(new StubDnsConfigService()); 401 } 402 #endif 403 404 } // namespace net 405