Reduce frequency of IPv6 probes in HostResolverImpl.

Currently HostResolverImpl tries to probe IPv6 support for every
Resolve() request, i.e. very often. The probe is implemented using
a dummy UDP socket. This doesn't look efficient because IPv6 state
doesn't change often.

Also this change will decrease frequency of the crash in the linked bug.
In that bug the root cause is that on OSX system certificate library may
close recently created file descriptors it doesn't own. Not creating
dummy UDP sockets very often will help to avoid hitting this bug in the
OS very often.

BUG=481163

Review URL: https://codereview.chromium.org/1138833003

Cr-Commit-Position: refs/heads/master@{#329885}
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index bd6e431..767a3727 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -74,6 +74,15 @@
 
 const char kLocalhost[] = "localhost.";
 
+// Time between IPv6 probes, i.e. for how long results of each IPv6 probe are
+// cached.
+const int kIPv6ProbePeriodMs = 1000;
+
+// Google DNS address used for IPv6 probes.
+const uint8_t kIPv6ProbeAddress[] =
+    { 0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88 };
+
 // We use a separate histogram name for each platform to facilitate the
 // display of error codes by their symbolic name (since each platform has
 // different mappings).
@@ -386,6 +395,15 @@
   return config->ToValue();
 }
 
+base::Value* NetLogIPv6AvailableCallback(bool ipv6_available,
+                                         bool cached,
+                                         NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue* dict = new base::DictionaryValue();
+  dict->SetBoolean("ipv6_available", ipv6_available);
+  dict->SetBoolean("cached", cached);
+  return dict;
+}
+
 // The logging routines are defined here because some requests are resolved
 // without a Request object.
 
@@ -1815,6 +1833,7 @@
       num_dns_failures_(0),
       probe_ipv6_support_(true),
       use_local_ipv6_(false),
+      last_ipv6_probe_result_(true),
       resolved_known_ipv6_hostname_(false),
       additional_resolver_flags_(0),
       fallback_to_proctask_(true),
@@ -2167,7 +2186,7 @@
 HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest(
     const RequestInfo& info,
     const IPAddressNumber* ip_number,
-    const BoundNetLog& net_log) const {
+    const BoundNetLog& net_log) {
   HostResolverFlags effective_flags =
       info.host_resolver_flags() | additional_resolver_flags_;
   AddressFamily effective_address_family = info.address_family();
@@ -2183,16 +2202,7 @@
         // set) so the code requesting the resolution should be amenable to
         // receiving a IPv6 resolution.
         ip_number == nullptr) {
-      // Google DNS address.
-      const uint8 kIPv6Address[] =
-          { 0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00,
-            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88 };
-      IPAddressNumber address(kIPv6Address,
-                              kIPv6Address + arraysize(kIPv6Address));
-      bool rv6 = IsGloballyReachable(address, net_log);
-      net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_IPV6_REACHABILITY_CHECK,
-                       NetLog::BoolCallback("ipv6_available", rv6));
-      if (!rv6) {
+      if (!IsIPv6Reachable(net_log)) {
         effective_address_family = ADDRESS_FAMILY_IPV4;
         effective_flags |= HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
       }
@@ -2210,6 +2220,22 @@
   return Key(hostname, effective_address_family, effective_flags);
 }
 
+bool HostResolverImpl::IsIPv6Reachable(const BoundNetLog& net_log) {
+  base::TimeTicks now = base::TimeTicks::Now();
+  bool cached = true;
+  if ((now - last_ipv6_probe_time_).InMilliseconds() > kIPv6ProbePeriodMs) {
+    IPAddressNumber address(kIPv6ProbeAddress,
+                            kIPv6ProbeAddress + arraysize(kIPv6ProbeAddress));
+    last_ipv6_probe_result_ = IsGloballyReachable(address, net_log);
+    last_ipv6_probe_time_ = now;
+    cached = false;
+  }
+  net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_IPV6_REACHABILITY_CHECK,
+                   base::Bind(&NetLogIPv6AvailableCallback,
+                              last_ipv6_probe_result_, cached));
+  return last_ipv6_probe_result_;
+}
+
 void HostResolverImpl::AbortAllInProgressJobs() {
   // In Abort, a Request callback could spawn new Jobs with matching keys, so
   // first collect and remove all running jobs from |jobs_|.
@@ -2279,6 +2305,7 @@
 
 void HostResolverImpl::OnIPAddressChanged() {
   resolved_known_ipv6_hostname_ = false;
+  last_ipv6_probe_time_ = base::TimeTicks();
   // Abandon all ProbeJobs.
   probe_weak_ptr_factory_.InvalidateWeakPtrs();
   if (cache_.get())