Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/dns/dns_config_service_win.h" | 5 #include "net/dns/dns_config_service_win.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 36 | 36 |
| 37 namespace net { | 37 namespace net { |
| 38 | 38 |
| 39 namespace internal { | 39 namespace internal { |
| 40 | 40 |
| 41 namespace { | 41 namespace { |
| 42 | 42 |
| 43 // Interval between retries to parse config. Used only until parsing succeeds. | 43 // Interval between retries to parse config. Used only until parsing succeeds. |
| 44 const int kRetryIntervalSeconds = 5; | 44 const int kRetryIntervalSeconds = 5; |
| 45 | 45 |
| 46 // Registry key paths. | |
| 47 const wchar_t* const kTcpipPath = | |
| 48 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"; | |
| 49 const wchar_t* const kTcpip6Path = | |
| 50 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters"; | |
| 51 const wchar_t* const kDnscachePath = | |
| 52 L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters"; | |
| 53 const wchar_t* const kPolicyPath = | |
| 54 L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient"; | |
| 46 const wchar_t* const kPrimaryDnsSuffixPath = | 55 const wchar_t* const kPrimaryDnsSuffixPath = |
| 47 L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient"; | 56 L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient"; |
| 57 const wchar_t* const kNRPTPath = | |
| 58 L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig"; | |
| 48 | 59 |
| 49 enum HostsParseWinResult { | 60 enum HostsParseWinResult { |
| 50 HOSTS_PARSE_WIN_OK = 0, | 61 HOSTS_PARSE_WIN_OK = 0, |
| 51 HOSTS_PARSE_WIN_UNREADABLE_HOSTS_FILE, | 62 HOSTS_PARSE_WIN_UNREADABLE_HOSTS_FILE, |
| 52 HOSTS_PARSE_WIN_COMPUTER_NAME_FAILED, | 63 HOSTS_PARSE_WIN_COMPUTER_NAME_FAILED, |
| 53 HOSTS_PARSE_WIN_IPHELPER_FAILED, | 64 HOSTS_PARSE_WIN_IPHELPER_FAILED, |
| 54 HOSTS_PARSE_WIN_BAD_ADDRESS, | 65 HOSTS_PARSE_WIN_BAD_ADDRESS, |
| 55 HOSTS_PARSE_WIN_MAX // Bounding values for enumeration. | 66 HOSTS_PARSE_WIN_MAX // Bounding values for enumeration. |
| 56 }; | 67 }; |
| 57 | 68 |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 191 | 202 |
| 192 if (!policy_reader.ReadDword(L"AppendToMultiLabelName", | 203 if (!policy_reader.ReadDword(L"AppendToMultiLabelName", |
| 193 &settings->append_to_multi_label_name)) { | 204 &settings->append_to_multi_label_name)) { |
| 194 return CONFIG_PARSE_WIN_READ_APPEND_MULTILABEL; | 205 return CONFIG_PARSE_WIN_READ_APPEND_MULTILABEL; |
| 195 } | 206 } |
| 196 | 207 |
| 197 if (!primary_dns_suffix_reader.ReadString(L"PrimaryDnsSuffix", | 208 if (!primary_dns_suffix_reader.ReadString(L"PrimaryDnsSuffix", |
| 198 &settings->primary_dns_suffix)) { | 209 &settings->primary_dns_suffix)) { |
| 199 return CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX; | 210 return CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX; |
| 200 } | 211 } |
| 212 | |
| 213 base::win::RegistryKeyIterator nrpt_rules(HKEY_LOCAL_MACHINE, kNRPTPath); | |
| 214 settings->have_name_resolution_policy = (nrpt_rules.SubkeyCount() > 0); | |
| 215 | |
| 201 return CONFIG_PARSE_WIN_OK; | 216 return CONFIG_PARSE_WIN_OK; |
| 202 } | 217 } |
| 203 | 218 |
| 204 // Default address of "localhost" and local computer name can be overridden | 219 // Default address of "localhost" and local computer name can be overridden |
| 205 // by the HOSTS file, but if it's not there, then we need to fill it in. | 220 // by the HOSTS file, but if it's not there, then we need to fill it in. |
| 206 HostsParseWinResult AddLocalhostEntries(DnsHosts* hosts) { | 221 HostsParseWinResult AddLocalhostEntries(DnsHosts* hosts) { |
| 207 const unsigned char kIPv4Localhost[] = { 127, 0, 0, 1 }; | 222 const unsigned char kIPv4Localhost[] = { 127, 0, 0, 1 }; |
| 208 const unsigned char kIPv6Localhost[] = { 0, 0, 0, 0, 0, 0, 0, 0, | 223 const unsigned char kIPv6Localhost[] = { 0, 0, 0, 0, 0, 0, 0, 0, |
| 209 0, 0, 0, 0, 0, 0, 0, 1 }; | 224 0, 0, 0, 0, 0, 0, 0, 1 }; |
| 210 IPAddressNumber loopback_ipv4(kIPv4Localhost, | 225 IPAddressNumber loopback_ipv4(kIPv4Localhost, |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 323 if (address.size() != kIPv6AddressSize) | 338 if (address.size() != kIPv6AddressSize) |
| 324 return false; | 339 return false; |
| 325 const uint8 kPrefix[] = { | 340 const uint8 kPrefix[] = { |
| 326 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, | 341 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, |
| 327 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 342 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 328 }; | 343 }; |
| 329 return std::equal(kPrefix, kPrefix + arraysize(kPrefix), | 344 return std::equal(kPrefix, kPrefix + arraysize(kPrefix), |
| 330 address.begin()) && (address.back() < 4); | 345 address.begin()) && (address.back() < 4); |
| 331 } | 346 } |
| 332 | 347 |
| 333 } // namespace | 348 // Returns the path to the HOSTS file. |
| 334 | |
| 335 base::FilePath GetHostsPath() { | 349 base::FilePath GetHostsPath() { |
| 336 TCHAR buffer[MAX_PATH]; | 350 TCHAR buffer[MAX_PATH]; |
| 337 UINT rc = GetSystemDirectory(buffer, MAX_PATH); | 351 UINT rc = GetSystemDirectory(buffer, MAX_PATH); |
| 338 DCHECK(0 < rc && rc < MAX_PATH); | 352 DCHECK(0 < rc && rc < MAX_PATH); |
| 339 return base::FilePath(buffer).Append( | 353 return base::FilePath(buffer).Append( |
| 340 FILE_PATH_LITERAL("drivers\\etc\\hosts")); | 354 FILE_PATH_LITERAL("drivers\\etc\\hosts")); |
| 341 } | 355 } |
| 342 | 356 |
| 357 void ConfigureSuffixSearch(const DnsSystemSettings& settings, | |
| 358 DnsConfig* config) { | |
|
mmenke
2013/09/16 16:34:46
I'm assuming this all exactly the same as before,
szym
2013/09/16 19:17:35
correct
| |
| 359 // SearchList takes precedence, so check it first. | |
| 360 if (settings.policy_search_list.set) { | |
| 361 std::vector<std::string> search; | |
| 362 if (ParseSearchList(settings.policy_search_list.value, &search)) { | |
| 363 config->search.swap(search); | |
| 364 return; | |
| 365 } | |
| 366 // Even if invalid, the policy disables the user-specified setting below. | |
| 367 } else if (settings.tcpip_search_list.set) { | |
| 368 std::vector<std::string> search; | |
| 369 if (ParseSearchList(settings.tcpip_search_list.value, &search)) { | |
| 370 config->search.swap(search); | |
| 371 return; | |
| 372 } | |
| 373 } | |
| 374 | |
| 375 // In absence of explicit search list, suffix search is: | |
| 376 // [primary suffix, connection-specific suffix, devolution of primary suffix]. | |
| 377 // Primary suffix can be set by policy (primary_dns_suffix) or | |
| 378 // user setting (tcpip_domain). | |
| 379 // | |
| 380 // The policy (primary_dns_suffix) can be edited via Group Policy Editor | |
| 381 // (gpedit.msc) at Local Computer Policy => Computer Configuration | |
| 382 // => Administrative Template => Network => DNS Client => Primary DNS Suffix. | |
| 383 // | |
| 384 // The user setting (tcpip_domain) can be configurred at Computer Name in | |
| 385 // System Settings | |
| 386 std::string primary_suffix; | |
| 387 if ((settings.primary_dns_suffix.set && | |
| 388 ParseDomainASCII(settings.primary_dns_suffix.value, &primary_suffix)) || | |
| 389 (settings.tcpip_domain.set && | |
| 390 ParseDomainASCII(settings.tcpip_domain.value, &primary_suffix))) { | |
| 391 // Primary suffix goes in front. | |
| 392 config->search.insert(config->search.begin(), primary_suffix); | |
| 393 } else { | |
| 394 return; // No primary suffix, hence no devolution. | |
| 395 } | |
| 396 | |
| 397 // Devolution is determined by precedence: policy > dnscache > tcpip. | |
| 398 // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel | |
| 399 // are overridden independently. | |
| 400 DnsSystemSettings::DevolutionSetting devolution = settings.policy_devolution; | |
| 401 | |
| 402 if (!devolution.enabled.set) | |
| 403 devolution.enabled = settings.dnscache_devolution.enabled; | |
| 404 if (!devolution.enabled.set) | |
| 405 devolution.enabled = settings.tcpip_devolution.enabled; | |
| 406 if (devolution.enabled.set && (devolution.enabled.value == 0)) | |
| 407 return; // Devolution disabled. | |
| 408 | |
| 409 // By default devolution is enabled. | |
| 410 | |
| 411 if (!devolution.level.set) | |
| 412 devolution.level = settings.dnscache_devolution.level; | |
| 413 if (!devolution.level.set) | |
| 414 devolution.level = settings.tcpip_devolution.level; | |
| 415 | |
| 416 // After the recent update, Windows will try to determine a safe default | |
| 417 // value by comparing the forest root domain (FRD) to the primary suffix. | |
| 418 // See http://support.microsoft.com/kb/957579 for details. | |
| 419 // For now, if the level is not set, we disable devolution, assuming that | |
| 420 // we will fallback to the system getaddrinfo anyway. This might cause | |
| 421 // performance loss for resolutions which depend on the system default | |
| 422 // devolution setting. | |
| 423 // | |
| 424 // If the level is explicitly set below 2, devolution is disabled. | |
| 425 if (!devolution.level.set || devolution.level.value < 2) | |
| 426 return; // Devolution disabled. | |
| 427 | |
| 428 // Devolve the primary suffix. This naive logic matches the observed | |
| 429 // behavior (see also ParseSearchList). If a suffix is not valid, it will be | |
| 430 // discarded when the fully-qualified name is converted to DNS format. | |
| 431 | |
| 432 unsigned num_dots = std::count(primary_suffix.begin(), | |
| 433 primary_suffix.end(), '.'); | |
| 434 | |
| 435 for (size_t offset = 0; num_dots >= devolution.level.value; --num_dots) { | |
| 436 offset = primary_suffix.find('.', offset + 1); | |
| 437 config->search.push_back(primary_suffix.substr(offset + 1)); | |
| 438 } | |
| 439 } | |
| 440 | |
| 441 } // namespace | |
| 442 | |
| 343 bool ParseSearchList(const base::string16& value, | 443 bool ParseSearchList(const base::string16& value, |
| 344 std::vector<std::string>* output) { | 444 std::vector<std::string>* output) { |
| 345 DCHECK(output); | 445 DCHECK(output); |
| 346 if (value.empty()) | 446 if (value.empty()) |
| 347 return false; | 447 return false; |
| 348 | 448 |
| 349 output->clear(); | 449 output->clear(); |
| 350 | 450 |
| 351 // If the list includes an empty hostname (",," or ", ,"), it is terminated. | 451 // If the list includes an empty hostname (",," or ", ,"), it is terminated. |
| 352 // Although nslookup and network connection property tab ignore such | 452 // Although nslookup and network connection property tab ignore such |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 422 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | 522 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
| 423 config->append_to_multi_label_name = false; | 523 config->append_to_multi_label_name = false; |
| 424 } else { | 524 } else { |
| 425 config->append_to_multi_label_name = true; | 525 config->append_to_multi_label_name = true; |
| 426 } | 526 } |
| 427 } else { | 527 } else { |
| 428 config->append_to_multi_label_name = | 528 config->append_to_multi_label_name = |
| 429 (settings.append_to_multi_label_name.value != 0); | 529 (settings.append_to_multi_label_name.value != 0); |
| 430 } | 530 } |
| 431 | 531 |
| 432 // SearchList takes precedence, so check it first. | 532 ConfigParseWinResult result = CONFIG_PARSE_WIN_OK; |
| 433 if (settings.policy_search_list.set) { | 533 if (settings.have_name_resolution_policy) { |
| 434 std::vector<std::string> search; | 534 config->unhandled_options = true; |
| 435 if (ParseSearchList(settings.policy_search_list.value, &search)) { | 535 // TODO(szym): only set this to true if NRPT has DirectAccess rules. |
| 436 config->search.swap(search); | 536 config->use_local_ipv6 = true; |
| 437 return CONFIG_PARSE_WIN_OK; | 537 result = CONFIG_PARSE_WIN_UNHANDLED_OPTIONS; |
| 438 } | |
| 439 // Even if invalid, the policy disables the user-specified setting below. | |
| 440 } else if (settings.tcpip_search_list.set) { | |
| 441 std::vector<std::string> search; | |
| 442 if (ParseSearchList(settings.tcpip_search_list.value, &search)) { | |
| 443 config->search.swap(search); | |
| 444 return CONFIG_PARSE_WIN_OK; | |
| 445 } | |
| 446 } | 538 } |
| 447 | 539 |
| 448 // In absence of explicit search list, suffix search is: | 540 ConfigureSuffixSearch(settings, config); |
| 449 // [primary suffix, connection-specific suffix, devolution of primary suffix]. | 541 return result; |
| 450 // Primary suffix can be set by policy (primary_dns_suffix) or | |
| 451 // user setting (tcpip_domain). | |
| 452 // | |
| 453 // The policy (primary_dns_suffix) can be edited via Group Policy Editor | |
| 454 // (gpedit.msc) at Local Computer Policy => Computer Configuration | |
| 455 // => Administrative Template => Network => DNS Client => Primary DNS Suffix. | |
| 456 // | |
| 457 // The user setting (tcpip_domain) can be configurred at Computer Name in | |
| 458 // System Settings | |
| 459 std::string primary_suffix; | |
| 460 if ((settings.primary_dns_suffix.set && | |
| 461 ParseDomainASCII(settings.primary_dns_suffix.value, &primary_suffix)) || | |
| 462 (settings.tcpip_domain.set && | |
| 463 ParseDomainASCII(settings.tcpip_domain.value, &primary_suffix))) { | |
| 464 // Primary suffix goes in front. | |
| 465 config->search.insert(config->search.begin(), primary_suffix); | |
| 466 } else { | |
| 467 return CONFIG_PARSE_WIN_OK; // No primary suffix, hence no devolution. | |
| 468 } | |
| 469 | |
| 470 // Devolution is determined by precedence: policy > dnscache > tcpip. | |
| 471 // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel | |
| 472 // are overridden independently. | |
| 473 DnsSystemSettings::DevolutionSetting devolution = settings.policy_devolution; | |
| 474 | |
| 475 if (!devolution.enabled.set) | |
| 476 devolution.enabled = settings.dnscache_devolution.enabled; | |
| 477 if (!devolution.enabled.set) | |
| 478 devolution.enabled = settings.tcpip_devolution.enabled; | |
| 479 if (devolution.enabled.set && (devolution.enabled.value == 0)) | |
| 480 return CONFIG_PARSE_WIN_OK; // Devolution disabled. | |
| 481 | |
| 482 // By default devolution is enabled. | |
| 483 | |
| 484 if (!devolution.level.set) | |
| 485 devolution.level = settings.dnscache_devolution.level; | |
| 486 if (!devolution.level.set) | |
| 487 devolution.level = settings.tcpip_devolution.level; | |
| 488 | |
| 489 // After the recent update, Windows will try to determine a safe default | |
| 490 // value by comparing the forest root domain (FRD) to the primary suffix. | |
| 491 // See http://support.microsoft.com/kb/957579 for details. | |
| 492 // For now, if the level is not set, we disable devolution, assuming that | |
| 493 // we will fallback to the system getaddrinfo anyway. This might cause | |
| 494 // performance loss for resolutions which depend on the system default | |
| 495 // devolution setting. | |
| 496 // | |
| 497 // If the level is explicitly set below 2, devolution is disabled. | |
| 498 if (!devolution.level.set || devolution.level.value < 2) | |
| 499 return CONFIG_PARSE_WIN_OK; // Devolution disabled. | |
| 500 | |
| 501 // Devolve the primary suffix. This naive logic matches the observed | |
| 502 // behavior (see also ParseSearchList). If a suffix is not valid, it will be | |
| 503 // discarded when the fully-qualified name is converted to DNS format. | |
| 504 | |
| 505 unsigned num_dots = std::count(primary_suffix.begin(), | |
| 506 primary_suffix.end(), '.'); | |
| 507 | |
| 508 for (size_t offset = 0; num_dots >= devolution.level.value; --num_dots) { | |
| 509 offset = primary_suffix.find('.', offset + 1); | |
| 510 config->search.push_back(primary_suffix.substr(offset + 1)); | |
| 511 } | |
| 512 return CONFIG_PARSE_WIN_OK; | |
| 513 } | 542 } |
| 514 | 543 |
| 515 // Watches registry and HOSTS file for changes. Must live on a thread which | 544 // Watches registry and HOSTS file for changes. Must live on a thread which |
| 516 // allows IO. | 545 // allows IO. |
| 517 class DnsConfigServiceWin::Watcher | 546 class DnsConfigServiceWin::Watcher |
| 518 : public NetworkChangeNotifier::IPAddressObserver { | 547 : public NetworkChangeNotifier::IPAddressObserver { |
| 519 public: | 548 public: |
| 520 explicit Watcher(DnsConfigServiceWin* service) : service_(service) {} | 549 explicit Watcher(DnsConfigServiceWin* service) : service_(service) {} |
| 521 ~Watcher() { | 550 ~Watcher() { |
| 522 NetworkChangeNotifier::RemoveIPAddressObserver(this); | 551 NetworkChangeNotifier::RemoveIPAddressObserver(this); |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 599 private: | 628 private: |
| 600 virtual ~ConfigReader() {} | 629 virtual ~ConfigReader() {} |
| 601 | 630 |
| 602 virtual void DoWork() OVERRIDE { | 631 virtual void DoWork() OVERRIDE { |
| 603 // Should be called on WorkerPool. | 632 // Should be called on WorkerPool. |
| 604 base::TimeTicks start_time = base::TimeTicks::Now(); | 633 base::TimeTicks start_time = base::TimeTicks::Now(); |
| 605 DnsSystemSettings settings = {}; | 634 DnsSystemSettings settings = {}; |
| 606 ConfigParseWinResult result = ReadSystemSettings(&settings); | 635 ConfigParseWinResult result = ReadSystemSettings(&settings); |
| 607 if (result == CONFIG_PARSE_WIN_OK) | 636 if (result == CONFIG_PARSE_WIN_OK) |
| 608 result = ConvertSettingsToDnsConfig(settings, &dns_config_); | 637 result = ConvertSettingsToDnsConfig(settings, &dns_config_); |
| 609 success_ = (result == CONFIG_PARSE_WIN_OK); | 638 success_ = (result == CONFIG_PARSE_WIN_OK || |
| 639 result == CONFIG_PARSE_WIN_UNHANDLED_OPTIONS); | |
| 610 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParseWin", | 640 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParseWin", |
| 611 result, CONFIG_PARSE_WIN_MAX); | 641 result, CONFIG_PARSE_WIN_MAX); |
| 612 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_); | 642 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_); |
| 613 UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration", | 643 UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration", |
| 614 base::TimeTicks::Now() - start_time); | 644 base::TimeTicks::Now() - start_time); |
| 615 } | 645 } |
| 616 | 646 |
| 617 virtual void OnWorkFinished() OVERRIDE { | 647 virtual void OnWorkFinished() OVERRIDE { |
| 618 DCHECK(loop()->BelongsToCurrentThread()); | 648 DCHECK(loop()->BelongsToCurrentThread()); |
| 619 DCHECK(!IsCancelled()); | 649 DCHECK(!IsCancelled()); |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 728 } | 758 } |
| 729 | 759 |
| 730 } // namespace internal | 760 } // namespace internal |
| 731 | 761 |
| 732 // static | 762 // static |
| 733 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { | 763 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { |
| 734 return scoped_ptr<DnsConfigService>(new internal::DnsConfigServiceWin()); | 764 return scoped_ptr<DnsConfigService>(new internal::DnsConfigServiceWin()); |
| 735 } | 765 } |
| 736 | 766 |
| 737 } // namespace net | 767 } // namespace net |
| OLD | NEW |