Reland "Track DoH probe results per-context"

Original: crrev.com/c/2001144
Revert: crrev.com/c/2050749

Was reverted due to crashes where
|ResolveContext::doh_server_availability_| size was not matching up with
the number of DoH servers in the config in |current_session_|. Because
the config is const in the session and |doh_server_availability_| is
only and always set when setting |current_session_|, the most plausible
theory I have is that the session pointer comparisons were invalid due
to memory reuse (new session but incorrectly recognized as the old
session because it has the same pointer).

Protected the |current_session_| pointer from reuse via WeakPtr. Not
ideal, but DnsSession ownership is already very loose with both ref
counting and other WeakPtr instances. Also added a bunch of CHECK
validations to help spot similar issues if this continues to or later
crashes similarly.

Bug: 1022059
Change-Id: Ibf030fa06882f02830bbaa13e45d9669815e9c5a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2050758
Commit-Queue: Eric Orth <[email protected]>
Reviewed-by: Matt Menke <[email protected]>
Cr-Commit-Position: refs/heads/master@{#741180}
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc
index 00b29f1..3910b7c 100644
--- a/net/dns/dns_test_util.cc
+++ b/net/dns/dns_test_util.cc
@@ -447,8 +447,9 @@
                   const std::string& hostname,
                   uint16_t qtype,
                   bool secure,
+                  bool force_doh_server_available,
                   DnsConfig::SecureDnsMode secure_dns_mode,
-                  URLRequestContext* url_request_context,
+                  ResolveContext* resolve_context,
                   DnsTransactionFactory::CallbackType callback)
       : result_(MockDnsClientRule::FAIL),
         hostname_(hostname),
@@ -456,70 +457,77 @@
         callback_(std::move(callback)),
         started_(false),
         delayed_(false) {
-    // Find the relevant rule which matches |qtype|, |secure|, prefix of
-    // |hostname|, and |url_request_context| (iff the rule context is not
-    // null).
-    for (size_t i = 0; i < rules.size(); ++i) {
-      const std::string& prefix = rules[i].prefix;
-      if ((rules[i].qtype == qtype) && (rules[i].secure == secure) &&
-          (hostname.size() >= prefix.size()) &&
-          (hostname.compare(0, prefix.size(), prefix) == 0) &&
-          (!rules[i].context || rules[i].context == url_request_context)) {
-        const MockDnsClientRule::Result* result = &rules[i].result;
-        result_ = MockDnsClientRule::Result(result->type);
-        delayed_ = rules[i].delay;
+    // Do not allow matching any rules if transaction is secure and no DoH
+    // servers are available.
+    if (!secure || force_doh_server_available ||
+        resolve_context->NumAvailableDohServers(
+            resolve_context->current_session_for_testing()) > 0) {
+      // Find the relevant rule which matches |qtype|, |secure|, prefix of
+      // |hostname|, and |url_request_context| (iff the rule context is not
+      // null).
+      for (size_t i = 0; i < rules.size(); ++i) {
+        const std::string& prefix = rules[i].prefix;
+        if ((rules[i].qtype == qtype) && (rules[i].secure == secure) &&
+            (hostname.size() >= prefix.size()) &&
+            (hostname.compare(0, prefix.size(), prefix) == 0) &&
+            (!rules[i].context ||
+             rules[i].context == resolve_context->url_request_context())) {
+          const MockDnsClientRule::Result* result = &rules[i].result;
+          result_ = MockDnsClientRule::Result(result->type);
+          delayed_ = rules[i].delay;
 
-        // Generate a DnsResponse when not provided with the rule.
-        std::vector<DnsResourceRecord> authority_records;
-        std::string dns_name;
-        CHECK(DNSDomainFromDot(hostname_, &dns_name));
-        base::Optional<DnsQuery> query(base::in_place, 22 /* id */, dns_name,
-                                       qtype_);
-        switch (result->type) {
-          case MockDnsClientRule::NODOMAIN:
-          case MockDnsClientRule::EMPTY:
-            DCHECK(!result->response);  // Not expected to be provided.
-            authority_records = {BuildSoaRecord(hostname_)};
-            result_.response = std::make_unique<DnsResponse>(
-                22 /* id */, false /* is_authoritative */,
-                std::vector<DnsResourceRecord>() /* answers */,
-                authority_records,
-                std::vector<DnsResourceRecord>() /* additional_records */,
-                query,
-                result->type == MockDnsClientRule::NODOMAIN
-                    ? dns_protocol::kRcodeNXDOMAIN
-                    : 0);
-            break;
-          case MockDnsClientRule::FAIL:
-          case MockDnsClientRule::TIMEOUT:
-            DCHECK(!result->response);  // Not expected to be provided.
-            break;
-          case MockDnsClientRule::OK:
-            if (result->response) {
-              // Copy response in case |rules| are destroyed before the
-              // transaction completes.
+          // Generate a DnsResponse when not provided with the rule.
+          std::vector<DnsResourceRecord> authority_records;
+          std::string dns_name;
+          CHECK(DNSDomainFromDot(hostname_, &dns_name));
+          base::Optional<DnsQuery> query(base::in_place, 22 /* id */, dns_name,
+                                         qtype_);
+          switch (result->type) {
+            case MockDnsClientRule::NODOMAIN:
+            case MockDnsClientRule::EMPTY:
+              DCHECK(!result->response);  // Not expected to be provided.
+              authority_records = {BuildSoaRecord(hostname_)};
               result_.response = std::make_unique<DnsResponse>(
-                  result->response->io_buffer(),
-                  result->response->io_buffer_size());
-              CHECK(result_.response->InitParseWithoutQuery(
-                  result->response->io_buffer_size()));
-            } else {
-              // Generated response only available for address types.
-              DCHECK(qtype_ == dns_protocol::kTypeA ||
-                     qtype_ == dns_protocol::kTypeAAAA);
-              result_.response = BuildTestDnsResponse(
-                  hostname_, qtype_ == dns_protocol::kTypeA
-                                 ? IPAddress::IPv4Localhost()
-                                 : IPAddress::IPv6Localhost());
-            }
-            break;
-          case MockDnsClientRule::MALFORMED:
-            DCHECK(!result->response);  // Not expected to be provided.
-            result_.response = CreateMalformedResponse(hostname_, qtype_);
-            break;
-        }
+                  22 /* id */, false /* is_authoritative */,
+                  std::vector<DnsResourceRecord>() /* answers */,
+                  authority_records,
+                  std::vector<DnsResourceRecord>() /* additional_records */,
+                  query,
+                  result->type == MockDnsClientRule::NODOMAIN
+                      ? dns_protocol::kRcodeNXDOMAIN
+                      : 0);
+              break;
+            case MockDnsClientRule::FAIL:
+            case MockDnsClientRule::TIMEOUT:
+              DCHECK(!result->response);  // Not expected to be provided.
+              break;
+            case MockDnsClientRule::OK:
+              if (result->response) {
+                // Copy response in case |rules| are destroyed before the
+                // transaction completes.
+                result_.response = std::make_unique<DnsResponse>(
+                    result->response->io_buffer(),
+                    result->response->io_buffer_size());
+                CHECK(result_.response->InitParseWithoutQuery(
+                    result->response->io_buffer_size()));
+              } else {
+                // Generated response only available for address types.
+                DCHECK(qtype_ == dns_protocol::kTypeA ||
+                       qtype_ == dns_protocol::kTypeAAAA);
+                result_.response = BuildTestDnsResponse(
+                    hostname_, qtype_ == dns_protocol::kTypeA
+                                   ? IPAddress::IPv4Localhost()
+                                   : IPAddress::IPv6Localhost());
+              }
+              break;
+            case MockDnsClientRule::MALFORMED:
+              DCHECK(!result->response);  // Not expected to be provided.
+              result_.response = CreateMalformedResponse(hostname_, qtype_);
+              break;
+          }
 
-        break;
+          break;
+        }
       }
     }
   }
@@ -618,8 +626,8 @@
     ResolveContext* resolve_context) {
   std::unique_ptr<MockTransaction> transaction =
       std::make_unique<MockTransaction>(
-          rules_, hostname, qtype, secure, secure_dns_mode,
-          resolve_context->url_request_context(), std::move(callback));
+          rules_, hostname, qtype, secure, force_doh_server_available_,
+          secure_dns_mode, resolve_context, std::move(callback));
   if (transaction->delayed())
     delayed_transactions_.push_back(transaction->AsWeakPtr());
   return transaction;
@@ -685,7 +693,10 @@
 
 bool MockDnsClient::FallbackFromSecureTransactionPreferred(
     ResolveContext* context) const {
-  return !CanUseSecureDnsTransactions() || !doh_server_available_;
+  bool doh_server_available =
+      force_doh_server_available_ ||
+      context->NumAvailableDohServers(session_.get()) > 0;
+  return !CanUseSecureDnsTransactions() || !doh_server_available;
 }
 
 bool MockDnsClient::FallbackFromInsecureTransactionPreferred() const {
@@ -752,8 +763,6 @@
   return overrides_;
 }
 
-void MockDnsClient::SetProbeSuccessForTest(unsigned index, bool success) {}
-
 void MockDnsClient::SetTransactionFactoryForTesting(
     std::unique_ptr<DnsTransactionFactory> factory) {
   NOTREACHED();
@@ -767,6 +776,11 @@
   return factory_->CompleteOneDelayedTransactionOfType(type);
 }
 
+void MockDnsClient::SetForceDohServerAvailable(bool available) {
+  force_doh_server_available_ = available;
+  factory_->set_force_doh_server_available(available);
+}
+
 base::Optional<DnsConfig> MockDnsClient::BuildEffectiveConfig() {
   if (overrides_.OverridesEverything())
     return overrides_.ApplyOverrides(DnsConfig());