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