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/lazy_instance.h" 14 #include "base/memory/scoped_ptr.h" 15 #include "base/metrics/histogram.h" 16 #include "base/time/time.h" 17 #include "net/base/ip_endpoint.h" 18 #include "net/base/net_util.h" 19 #include "net/dns/dns_hosts.h" 20 #include "net/dns/dns_protocol.h" 21 #include "net/dns/notify_watcher_mac.h" 22 #include "net/dns/serial_worker.h" 23 24 #if defined(OS_MACOSX) && !defined(OS_IOS) 25 #include "net/dns/dns_config_watcher_mac.h" 26 #endif 27 28 #if defined(OS_ANDROID) 29 #include <sys/system_properties.h> 30 #include "net/base/network_change_notifier.h" 31 #endif 32 33 namespace net { 34 35 namespace internal { 36 37 namespace { 38 39 #if !defined(OS_ANDROID) 40 const base::FilePath::CharType* kFilePathHosts = 41 FILE_PATH_LITERAL("/etc/hosts"); 42 #else 43 const base::FilePath::CharType* kFilePathHosts = 44 FILE_PATH_LITERAL("/system/etc/hosts"); 45 #endif 46 47 #if defined(OS_IOS) 48 49 // There is no public API to watch the DNS configuration on iOS. 50 class DnsConfigWatcher { 51 public: 52 typedef base::Callback<void(bool succeeded)> CallbackType; 53 54 bool Watch(const CallbackType& callback) { 55 return false; 56 } 57 }; 58 59 #elif defined(OS_ANDROID) 60 // On Android, assume DNS config may have changed on every network change. 61 class DnsConfigWatcher : public NetworkChangeNotifier::NetworkChangeObserver { 62 public: 63 DnsConfigWatcher() { 64 NetworkChangeNotifier::AddNetworkChangeObserver(this); 65 } 66 67 virtual ~DnsConfigWatcher() { 68 NetworkChangeNotifier::RemoveNetworkChangeObserver(this); 69 } 70 71 bool Watch(const base::Callback<void(bool succeeded)>& callback) { 72 callback_ = callback; 73 return true; 74 } 75 76 virtual void OnNetworkChanged(NetworkChangeNotifier::ConnectionType type) 77 OVERRIDE { 78 if (!callback_.is_null() && type != NetworkChangeNotifier::CONNECTION_NONE) 79 callback_.Run(true); 80 } 81 82 private: 83 base::Callback<void(bool succeeded)> callback_; 84 }; 85 #elif !defined(OS_MACOSX) 86 // DnsConfigWatcher for OS_MACOSX is in dns_config_watcher_mac.{hh,cc}. 87 88 #ifndef _PATH_RESCONF // Normally defined in <resolv.h> 89 #define _PATH_RESCONF "/etc/resolv.conf" 90 #endif 91 92 static const base::FilePath::CharType* kFilePathConfig = 93 FILE_PATH_LITERAL(_PATH_RESCONF); 94 95 class DnsConfigWatcher { 96 public: 97 typedef base::Callback<void(bool succeeded)> CallbackType; 98 99 bool Watch(const CallbackType& callback) { 100 callback_ = callback; 101 return watcher_.Watch(base::FilePath(kFilePathConfig), false, 102 base::Bind(&DnsConfigWatcher::OnCallback, 103 base::Unretained(this))); 104 } 105 106 private: 107 void OnCallback(const base::FilePath& path, bool error) { 108 callback_.Run(!error); 109 } 110 111 base::FilePathWatcher watcher_; 112 CallbackType callback_; 113 }; 114 #endif 115 116 #if !defined(OS_ANDROID) 117 ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) { 118 ConfigParsePosixResult result; 119 config->unhandled_options = false; 120 #if defined(OS_OPENBSD) 121 // Note: res_ninit in glibc always returns 0 and sets RES_INIT. 122 // res_init behaves the same way. 123 memset(&_res, 0, sizeof(_res)); 124 if (res_init() == 0) { 125 result = ConvertResStateToDnsConfig(_res, config); 126 } else { 127 result = CONFIG_PARSE_POSIX_RES_INIT_FAILED; 128 } 129 #else // all other OS_POSIX 130 struct __res_state res; 131 memset(&res, 0, sizeof(res)); 132 if (res_ninit(&res) == 0) { 133 result = ConvertResStateToDnsConfig(res, config); 134 } else { 135 result = CONFIG_PARSE_POSIX_RES_INIT_FAILED; 136 } 137 // Prefer res_ndestroy where available. 138 #if defined(OS_MACOSX) || defined(OS_FREEBSD) 139 res_ndestroy(&res); 140 #else 141 res_nclose(&res); 142 #endif 143 #endif 144 145 #if defined(OS_MACOSX) && !defined(OS_IOS) 146 ConfigParsePosixResult error = DnsConfigWatcher::CheckDnsConfig(); 147 switch (error) { 148 case CONFIG_PARSE_POSIX_OK: 149 break; 150 case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS: 151 LOG(WARNING) << "dns_config has unhandled options!"; 152 config->unhandled_options = true; 153 default: 154 return error; 155 } 156 #endif // defined(OS_MACOSX) && !defined(OS_IOS) 157 // Override timeout value to match default setting on Windows. 158 config->timeout = base::TimeDelta::FromSeconds(kDnsTimeoutSeconds); 159 return result; 160 } 161 #else // defined(OS_ANDROID) 162 // Theoretically, this is bad. __system_property_get is not a supported API 163 // (but it's currently visible to anyone using Bionic), and the properties 164 // are implementation details that may disappear in future Android releases. 165 // Practically, libcutils provides property_get, which is a public API, and the 166 // DNS code (and its clients) are already robust against failing to get the DNS 167 // config for whatever reason, so the properties can disappear and the world 168 // won't end. 169 // TODO(ttuttle): Depend on libcutils, then switch this (and other uses of 170 // __system_property_get) to property_get. 171 ConfigParsePosixResult ReadDnsConfig(DnsConfig* dns_config) { 172 std::string dns1_string, dns2_string; 173 char property_value[PROP_VALUE_MAX]; 174 __system_property_get("net.dns1", property_value); 175 dns1_string = property_value; 176 __system_property_get("net.dns2", property_value); 177 dns2_string = property_value; 178 if (dns1_string.length() == 0 && dns2_string.length() == 0) 179 return CONFIG_PARSE_POSIX_NO_NAMESERVERS; 180 181 IPAddressNumber dns1_number, dns2_number; 182 bool parsed1 = ParseIPLiteralToNumber(dns1_string, &dns1_number); 183 bool parsed2 = ParseIPLiteralToNumber(dns2_string, &dns2_number); 184 if (!parsed1 && !parsed2) 185 return CONFIG_PARSE_POSIX_BAD_ADDRESS; 186 187 if (parsed1) { 188 IPEndPoint dns1(dns1_number, dns_protocol::kDefaultPort); 189 dns_config->nameservers.push_back(dns1); 190 } 191 if (parsed2) { 192 IPEndPoint dns2(dns2_number, dns_protocol::kDefaultPort); 193 dns_config->nameservers.push_back(dns2); 194 } 195 196 return CONFIG_PARSE_POSIX_OK; 197 } 198 #endif 199 200 } // namespace 201 202 class DnsConfigServicePosix::Watcher { 203 public: 204 explicit Watcher(DnsConfigServicePosix* service) 205 : service_(service), 206 weak_factory_(this) {} 207 ~Watcher() {} 208 209 bool Watch() { 210 bool success = true; 211 if (!config_watcher_.Watch(base::Bind(&Watcher::OnConfigChanged, 212 base::Unretained(this)))) { 213 LOG(ERROR) << "DNS config watch failed to start."; 214 success = false; 215 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", 216 DNS_CONFIG_WATCH_FAILED_TO_START_CONFIG, 217 DNS_CONFIG_WATCH_MAX); 218 } 219 if (!hosts_watcher_.Watch(base::FilePath(kFilePathHosts), false, 220 base::Bind(&Watcher::OnHostsChanged, 221 base::Unretained(this)))) { 222 LOG(ERROR) << "DNS hosts watch failed to start."; 223 success = false; 224 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", 225 DNS_CONFIG_WATCH_FAILED_TO_START_HOSTS, 226 DNS_CONFIG_WATCH_MAX); 227 } 228 return success; 229 } 230 231 private: 232 void OnConfigChanged(bool succeeded) { 233 // Ignore transient flutter of resolv.conf by delaying the signal a bit. 234 const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(50); 235 base::MessageLoop::current()->PostDelayedTask( 236 FROM_HERE, 237 base::Bind(&Watcher::OnConfigChangedDelayed, 238 weak_factory_.GetWeakPtr(), 239 succeeded), 240 kDelay); 241 } 242 void OnConfigChangedDelayed(bool succeeded) { 243 service_->OnConfigChanged(succeeded); 244 } 245 void OnHostsChanged(const base::FilePath& path, bool error) { 246 service_->OnHostsChanged(!error); 247 } 248 249 DnsConfigServicePosix* service_; 250 DnsConfigWatcher config_watcher_; 251 base::FilePathWatcher hosts_watcher_; 252 253 base::WeakPtrFactory<Watcher> weak_factory_; 254 255 DISALLOW_COPY_AND_ASSIGN(Watcher); 256 }; 257 258 // A SerialWorker that uses libresolv to initialize res_state and converts 259 // it to DnsConfig (except on Android, where it reads system properties 260 // net.dns1 and net.dns2; see #if around ReadDnsConfig above.) 261 class DnsConfigServicePosix::ConfigReader : public SerialWorker { 262 public: 263 explicit ConfigReader(DnsConfigServicePosix* service) 264 : service_(service), success_(false) {} 265 266 virtual void DoWork() OVERRIDE { 267 base::TimeTicks start_time = base::TimeTicks::Now(); 268 ConfigParsePosixResult result = ReadDnsConfig(&dns_config_); 269 switch (result) { 270 case CONFIG_PARSE_POSIX_MISSING_OPTIONS: 271 case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS: 272 DCHECK(dns_config_.unhandled_options); 273 // Fall through. 274 case CONFIG_PARSE_POSIX_OK: 275 success_ = true; 276 break; 277 default: 278 success_ = false; 279 break; 280 } 281 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParsePosix", 282 result, CONFIG_PARSE_POSIX_MAX); 283 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_); 284 UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration", 285 base::TimeTicks::Now() - start_time); 286 } 287 288 virtual void OnWorkFinished() OVERRIDE { 289 DCHECK(!IsCancelled()); 290 if (success_) { 291 service_->OnConfigRead(dns_config_); 292 } else { 293 LOG(WARNING) << "Failed to read DnsConfig."; 294 } 295 } 296 297 private: 298 virtual ~ConfigReader() {} 299 300 DnsConfigServicePosix* service_; 301 // Written in DoWork, read in OnWorkFinished, no locking necessary. 302 DnsConfig dns_config_; 303 bool success_; 304 305 DISALLOW_COPY_AND_ASSIGN(ConfigReader); 306 }; 307 308 // A SerialWorker that reads the HOSTS file and runs Callback. 309 class DnsConfigServicePosix::HostsReader : public SerialWorker { 310 public: 311 explicit HostsReader(DnsConfigServicePosix* service) 312 : service_(service), path_(kFilePathHosts), success_(false) {} 313 314 private: 315 virtual ~HostsReader() {} 316 317 virtual void DoWork() OVERRIDE { 318 base::TimeTicks start_time = base::TimeTicks::Now(); 319 success_ = ParseHostsFile(path_, &hosts_); 320 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_); 321 UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration", 322 base::TimeTicks::Now() - start_time); 323 } 324 325 virtual void OnWorkFinished() OVERRIDE { 326 if (success_) { 327 service_->OnHostsRead(hosts_); 328 } else { 329 LOG(WARNING) << "Failed to read DnsHosts."; 330 } 331 } 332 333 DnsConfigServicePosix* service_; 334 const base::FilePath path_; 335 // Written in DoWork, read in OnWorkFinished, no locking necessary. 336 DnsHosts hosts_; 337 bool success_; 338 339 DISALLOW_COPY_AND_ASSIGN(HostsReader); 340 }; 341 342 DnsConfigServicePosix::DnsConfigServicePosix() 343 : config_reader_(new ConfigReader(this)), 344 hosts_reader_(new HostsReader(this)) {} 345 346 DnsConfigServicePosix::~DnsConfigServicePosix() { 347 config_reader_->Cancel(); 348 hosts_reader_->Cancel(); 349 } 350 351 void DnsConfigServicePosix::ReadNow() { 352 config_reader_->WorkNow(); 353 hosts_reader_->WorkNow(); 354 } 355 356 bool DnsConfigServicePosix::StartWatching() { 357 // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139 358 watcher_.reset(new Watcher(this)); 359 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED, 360 DNS_CONFIG_WATCH_MAX); 361 return watcher_->Watch(); 362 } 363 364 void DnsConfigServicePosix::OnConfigChanged(bool succeeded) { 365 InvalidateConfig(); 366 if (succeeded) { 367 config_reader_->WorkNow(); 368 } else { 369 LOG(ERROR) << "DNS config watch failed."; 370 set_watch_failed(true); 371 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", 372 DNS_CONFIG_WATCH_FAILED_CONFIG, 373 DNS_CONFIG_WATCH_MAX); 374 } 375 } 376 377 void DnsConfigServicePosix::OnHostsChanged(bool succeeded) { 378 InvalidateHosts(); 379 if (succeeded) { 380 hosts_reader_->WorkNow(); 381 } else { 382 LOG(ERROR) << "DNS hosts watch failed."; 383 set_watch_failed(true); 384 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", 385 DNS_CONFIG_WATCH_FAILED_HOSTS, 386 DNS_CONFIG_WATCH_MAX); 387 } 388 } 389 390 #if !defined(OS_ANDROID) 391 ConfigParsePosixResult ConvertResStateToDnsConfig(const struct __res_state& res, 392 DnsConfig* dns_config) { 393 CHECK(dns_config != NULL); 394 if (!(res.options & RES_INIT)) 395 return CONFIG_PARSE_POSIX_RES_INIT_UNSET; 396 397 dns_config->nameservers.clear(); 398 399 #if defined(OS_MACOSX) || defined(OS_FREEBSD) 400 union res_sockaddr_union addresses[MAXNS]; 401 int nscount = res_getservers(const_cast<res_state>(&res), addresses, MAXNS); 402 DCHECK_GE(nscount, 0); 403 DCHECK_LE(nscount, MAXNS); 404 for (int i = 0; i < nscount; ++i) { 405 IPEndPoint ipe; 406 if (!ipe.FromSockAddr( 407 reinterpret_cast<const struct sockaddr*>(&addresses[i]), 408 sizeof addresses[i])) { 409 return CONFIG_PARSE_POSIX_BAD_ADDRESS; 410 } 411 dns_config->nameservers.push_back(ipe); 412 } 413 #elif defined(OS_LINUX) 414 COMPILE_ASSERT(arraysize(res.nsaddr_list) >= MAXNS && 415 arraysize(res._u._ext.nsaddrs) >= MAXNS, 416 incompatible_libresolv_res_state); 417 DCHECK_LE(res.nscount, MAXNS); 418 // Initially, glibc stores IPv6 in |_ext.nsaddrs| and IPv4 in |nsaddr_list|. 419 // In res_send.c:res_nsend, it merges |nsaddr_list| into |nsaddrs|, 420 // but we have to combine the two arrays ourselves. 421 for (int i = 0; i < res.nscount; ++i) { 422 IPEndPoint ipe; 423 const struct sockaddr* addr = NULL; 424 size_t addr_len = 0; 425 if (res.nsaddr_list[i].sin_family) { // The indicator used by res_nsend. 426 addr = reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]); 427 addr_len = sizeof res.nsaddr_list[i]; 428 } else if (res._u._ext.nsaddrs[i] != NULL) { 429 addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]); 430 addr_len = sizeof *res._u._ext.nsaddrs[i]; 431 } else { 432 return CONFIG_PARSE_POSIX_BAD_EXT_STRUCT; 433 } 434 if (!ipe.FromSockAddr(addr, addr_len)) 435 return CONFIG_PARSE_POSIX_BAD_ADDRESS; 436 dns_config->nameservers.push_back(ipe); 437 } 438 #else // !(defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FREEBSD)) 439 DCHECK_LE(res.nscount, MAXNS); 440 for (int i = 0; i < res.nscount; ++i) { 441 IPEndPoint ipe; 442 if (!ipe.FromSockAddr( 443 reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]), 444 sizeof res.nsaddr_list[i])) { 445 return CONFIG_PARSE_POSIX_BAD_ADDRESS; 446 } 447 dns_config->nameservers.push_back(ipe); 448 } 449 #endif 450 451 dns_config->search.clear(); 452 for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) { 453 dns_config->search.push_back(std::string(res.dnsrch[i])); 454 } 455 456 dns_config->ndots = res.ndots; 457 dns_config->timeout = base::TimeDelta::FromSeconds(res.retrans); 458 dns_config->attempts = res.retry; 459 #if defined(RES_ROTATE) 460 dns_config->rotate = res.options & RES_ROTATE; 461 #endif 462 dns_config->edns0 = res.options & RES_USE_EDNS0; 463 464 // The current implementation assumes these options are set. They normally 465 // cannot be overwritten by /etc/resolv.conf 466 unsigned kRequiredOptions = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; 467 if ((res.options & kRequiredOptions) != kRequiredOptions) { 468 dns_config->unhandled_options = true; 469 return CONFIG_PARSE_POSIX_MISSING_OPTIONS; 470 } 471 472 unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC; 473 if (res.options & kUnhandledOptions) { 474 dns_config->unhandled_options = true; 475 return CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS; 476 } 477 478 if (dns_config->nameservers.empty()) 479 return CONFIG_PARSE_POSIX_NO_NAMESERVERS; 480 481 // If any name server is 0.0.0.0, assume the configuration is invalid. 482 // TODO(szym): Measure how often this happens. http://crbug.com/125599 483 const IPAddressNumber kEmptyAddress(kIPv4AddressSize); 484 for (unsigned i = 0; i < dns_config->nameservers.size(); ++i) { 485 if (dns_config->nameservers[i].address() == kEmptyAddress) 486 return CONFIG_PARSE_POSIX_NULL_ADDRESS; 487 } 488 return CONFIG_PARSE_POSIX_OK; 489 } 490 #endif // !defined(OS_ANDROID) 491 492 } // namespace internal 493 494 // static 495 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { 496 return scoped_ptr<DnsConfigService>(new internal::DnsConfigServicePosix()); 497 } 498 499 } // namespace net 500