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 |