Repository: envoyproxy/envoy
Pull request #38879: c-ares: implement ares_reinit() to optimally handle the situation whe…
Author: login: ClifHouck
Created: 2025-03-25T01:03:31Z | Merged: 2025-03-26T17:45:33Z

…re DNS resolver needs to be re-initialized

Solves #34785

Co-authored-by: Rohit Agrawal <rohit.agrawal@databricks.com>
Co-authored-by: Clif Houck <me@clifhouck.com>


Diff:
diff --git a/changelogs/current.yaml b/changelogs/current.yaml
index 7f00599f73b6f..4ce9fd2e4b3d4 100644
--- a/changelogs/current.yaml
+++ b/changelogs/current.yaml
@@ -359,5 +359,8 @@ new_features:
   change: |
     Added envoy_v3_api_field_extensions.upstreams.http.v3.Http3ProtocolOptions.disable_qpack, an experimental support for
     disabling QPACK compression.
+- area: cares
+  change: |
+    Implemented ``ares_reinit()`` to optimally handle the situation where DNS resolver needs to be re-initialized.
 
 deprecated:
diff --git a/source/extensions/network/dns_resolver/cares/dns_impl.cc b/source/extensions/network/dns_resolver/cares/dns_impl.cc
index 2993736bbfe50..0896963c7d779 100644
--- a/source/extensions/network/dns_resolver/cares/dns_impl.cc
+++ b/source/extensions/network/dns_resolver/cares/dns_impl.cc
@@ -149,8 +149,6 @@ bool DnsResolverImpl::isCaresDefaultTheOnlyNameserver() {
 }
 
 void DnsResolverImpl::initializeChannel(ares_options* options, int optmask) {
-  dirty_channel_ = false;
-
   options->sock_state_cb = [](void* arg, os_fd_t fd, int read, int write) {
     static_cast<DnsResolverImpl*>(arg)->onAresSocketStateChange(fd, read, write);
   };
@@ -195,8 +193,9 @@ void DnsResolverImpl::AddrInfoPendingResolution::onAresGetAddrInfoCallback(
                       "dns resolution for {} failed with c-ares status {:#06x}: \"{}\"", dns_name_,
                       status, ares_strerror(status));
     } else {
-      ENVOY_LOG_EVENT(debug, "cares_resolution_no_records", "dns resolution without records for {}",
-                      dns_name_);
+      ENVOY_LOG_EVENT(debug, "cares_resolution_no_records",
+                      "dns resolution without records for {} c-ares status {:#06x}: \"{}\"",
+                      dns_name_, status, ares_strerror(status));
     }
   }
 
@@ -226,17 +225,10 @@ void DnsResolverImpl::AddrInfoPendingResolution::onAresGetAddrInfoCallback(
     completed_ = true;
 
     // If c-ares returns ARES_ECONNREFUSED and there is no fallback we assume that the channel_ is
-    // broken. Mark the channel dirty so that it is destroyed and reinitialized on a subsequent call
-    // to DnsResolver::resolve(). The optimal solution would be for c-ares to reinitialize the
-    // channel, and not have Envoy track side effects.
-    // context: https://github.com/envoyproxy/envoy/issues/4543 and
-    // https://github.com/c-ares/c-ares/issues/301.
-    //
-    // The channel cannot be destroyed and reinitialized here because that leads to a c-ares
-    // segfault.
+    // broken and hence we reinitialize it here.
     if (status == ARES_ECONNREFUSED || status == ARES_EREFUSED || status == ARES_ESERVFAIL ||
         status == ARES_ENOTIMP) {
-      parent_.dirty_channel_ = true;
+      parent_.reinitializeChannel();
     }
   }
 
@@ -331,7 +323,7 @@ void DnsResolverImpl::AddrInfoPendingResolution::onAresGetAddrInfoCallback(
 
 void DnsResolverImpl::PendingResolution::finishResolve() {
   ENVOY_LOG_EVENT(debug, "cares_dns_resolution_complete",
-                  "dns resolution for {} completed with status {} and details {}", dns_name_,
+                  "dns resolution for {} completed with status {:#06x}: \"{}\"", dns_name_,
                   static_cast<int>(pending_response_.status_), pending_response_.details_);
 
   if (!cancelled_) {
@@ -413,6 +405,38 @@ void DnsResolverImpl::onAresSocketStateChange(os_fd_t fd, int read, int write) {
                           (write ? Event::FileReadyType::Write : 0));
 }
 
+void DnsResolverImpl::reinitializeChannel() {
+  AresOptions options = defaultAresOptions();
+
+  // Set up the options for re-initialization
+  options.options_.sock_state_cb = [](void* arg, os_fd_t fd, int read, int write) {
+    static_cast<DnsResolverImpl*>(arg)->onAresSocketStateChange(fd, read, write);
+  };
+  options.options_.sock_state_cb_data = this;
+  options.optmask_ |= ARES_OPT_SOCK_STATE_CB;
+
+  // Reinitialize the channel with the new options
+  int result = ares_reinit(channel_);
+  RELEASE_ASSERT(result == ARES_SUCCESS, "c-ares channel re-initialization failed");
+  stats_.reinits_.inc();
+  ENVOY_LOG_EVENT(debug, "cares_channel_reinitialized",
+                  "Reinitialized cares channel via ares_reinit");
+
+  if (resolvers_csv_.has_value()) {
+    bool use_resolvers = true;
+    // If the only name server available is c-ares' default then fallback to the user defined
+    // resolvers. Otherwise, use the resolvers provided by c-ares.
+    if (use_resolvers_as_fallback_ && !isCaresDefaultTheOnlyNameserver()) {
+      use_resolvers = false;
+    }
+
+    if (use_resolvers) {
+      result = ares_set_servers_ports_csv(channel_, resolvers_csv_->c_str());
+      RELEASE_ASSERT(result == ARES_SUCCESS, "c-ares set servers failed during re-initialization");
+    }
+  }
+}
+
 ActiveDnsQuery* DnsResolverImpl::resolve(const std::string& dns_name,
                                          DnsLookupFamily dns_lookup_family, ResolveCb callback) {
   ENVOY_LOG_EVENT(debug, "cares_dns_resolution_start", "dns resolution for {} started", dns_name);
@@ -421,13 +445,6 @@ ActiveDnsQuery* DnsResolverImpl::resolve(const std::string& dns_name,
   // failed initial call to getAddrInfo followed by a synchronous IPv4
   // resolution.
 
-  // @see DnsResolverImpl::PendingResolution::onAresGetAddrInfoCallback for why this is done.
-  if (dirty_channel_) {
-    ares_destroy(channel_);
-    AresOptions options = defaultAresOptions();
-    initializeChannel(&options.options_, options.optmask_);
-  }
-
   auto pending_resolution = std::make_unique<AddrInfoPendingResolution>(
       *this, callback, dispatcher_, channel_, dns_name, dns_lookup_family);
   pending_resolution->startResolution();
diff --git a/source/extensions/network/dns_resolver/cares/dns_impl.h b/source/extensions/network/dns_resolver/cares/dns_impl.h
index 41ff523a1bd3e..20b772853e399 100644
--- a/source/extensions/network/dns_resolver/cares/dns_impl.h
+++ b/source/extensions/network/dns_resolver/cares/dns_impl.h
@@ -28,7 +28,8 @@ namespace Network {
   GAUGE(pending_resolutions, NeverImport)                                                          \
   COUNTER(not_found)                                                                               \
   COUNTER(get_addr_failure)                                                                        \
-  COUNTER(timeouts)
+  COUNTER(timeouts)                                                                                \
+  COUNTER(reinits)
 
 /**
  * Struct definition for all DNS stats. @see stats_macros.h
@@ -59,10 +60,7 @@ class DnsResolverImpl : public DnsResolver, protected Logger::Loggable<Logger::I
   // Network::DnsResolver
   ActiveDnsQuery* resolve(const std::string& dns_name, DnsLookupFamily dns_lookup_family,
                           ResolveCb callback) override;
-  void resetNetworking() override {
-    // Dirty the channel so that the next query will recreate it.
-    dirty_channel_ = true;
-  }
+  void resetNetworking() override { reinitializeChannel(); }
 
 private:
   friend class DnsResolverImplPeer;
@@ -159,7 +157,7 @@ class DnsResolverImpl : public DnsResolver, protected Logger::Loggable<Logger::I
     // perform resolutions on both families concurrently.
     bool dual_resolution_ = false;
     // The number of outstanding pending resolutions. This should always be 0 or 1 for all lookup
-    // types other than All. For all, this can be be 0-2 as both queries are issued in parallel.
+    // types other than All. For all, this can be 0-2 as both queries are issued in parallel.
     // This is used mostly for assertions but is used in the ARES_EDESTRUCTION path to make sure
     // all concurrent queries are unwound before cleaning up the resolution.
     uint32_t pending_resolutions_ = 0;
@@ -179,6 +177,8 @@ class DnsResolverImpl : public DnsResolver, protected Logger::Loggable<Logger::I
   // c-ares callback when a socket state changes, indicating that libevent
   // should listen for read/write events.
   void onAresSocketStateChange(os_fd_t fd, int read, int write);
+  // Re-initialize the c-ares channel.
+  void reinitializeChannel();
   // Initialize the channel.
   void initializeChannel(ares_options* options, int optmask);
   // Check if the only nameserver available is the c-ares default.
@@ -193,7 +193,6 @@ class DnsResolverImpl : public DnsResolver, protected Logger::Loggable<Logger::I
   Event::Dispatcher& dispatcher_;
   Event::TimerPtr timer_;
   ares_channel channel_;
-  bool dirty_channel_{};
   envoy::config::core::v3::DnsResolverOptions dns_resolver_options_;
 
   absl::node_hash_map<int, Event::FileEventPtr> events_;
diff --git a/test/extensions/network/dns_resolver/cares/dns_impl_test.cc b/test/extensions/network/dns_resolver/cares/dns_impl_test.cc
index 98e297bafd2bb..3c054a3831163 100644
--- a/test/extensions/network/dns_resolver/cares/dns_impl_test.cc
+++ b/test/extensions/network/dns_resolver/cares/dns_impl_test.cc
@@ -241,11 +241,11 @@ class TestDnsServerQuery {
         DNS_HEADER_SET_RCODE(response_base, REFUSED);
       } else if (q_type == T_A && parent_.error_on_a_) {
         // Use `FORMERR` here as a most of the error codes (`SERVFAIL`, `NOTIMP`, `REFUSED`) result
-        // in a dirty channel. See `DnsImplTest::DestroyChannelOnRefused` for details.
+        // in a reinitialized channel. See `DnsImplTest::DestroyChannelOnRefused` for details.
         DNS_HEADER_SET_RCODE(response_base, FORMERR);
       } else if (q_type == T_AAAA && parent_.error_on_aaaa_) {
         // Use `FORMERR` here as a most of the error codes (`SERVFAIL`, `NOTIMP`, `REFUSED`) result
-        // in a dirty channel. See `DnsImplTest::DestroyChannelOnRefused` for details.
+        // in a reinitialized channel. See `DnsImplTest::DestroyChannelOnRefused` for details.
         DNS_HEADER_SET_RCODE(response_base, FORMERR);
       } else {
         DNS_HEADER_SET_RCODE(response_base, answer_count > 0 ? NOERROR : NXDOMAIN);
@@ -403,7 +403,6 @@ class DnsResolverImplPeer {
   DnsResolverImplPeer(DnsResolverImpl* resolver) : resolver_(resolver) {}
 
   ares_channel channel() const { return resolver_->channel_; }
-  bool isChannelDirty() const { return resolver_->dirty_channel_; }
   const absl::node_hash_map<int, Event::FileEventPtr>& events() { return resolver_->events_; }
   // Reset the channel state for a DnsResolverImpl such that it will only use
   // TCP and optionally has a zero timeout (for validating timeout behavior).
@@ -956,7 +955,7 @@ class DnsImplTest : public testing::TestWithParam<Address::IpVersion> {
   }
 
   void checkStats(uint64_t resolve_total, uint64_t pending_resolutions, uint64_t not_found,
-                  uint64_t get_addr_failure, uint64_t timeouts) {
+                  uint64_t get_addr_failure, uint64_t timeouts, uint64_t reinits) {
     EXPECT_EQ(resolve_total, stats_store_.counter("dns.cares.resolve_total").value());
     EXPECT_EQ(
         pending_resolutions,
@@ -965,6 +964,7 @@ class DnsImplTest : public testing::TestWithParam<Address::IpVersion> {
     EXPECT_EQ(not_found, stats_store_.counter("dns.cares.not_found").value());
     EXPECT_EQ(get_addr_failure, stats_store_.counter("dns.cares.get_addr_failure").value());
     EXPECT_EQ(timeouts, stats_store_.counter("dns.cares.timeouts").value());
+    EXPECT_EQ(reinits, stats_store_.counter("dns.cares.reinits").value());
   }
 
 protected:
@@ -1007,6 +1007,7 @@ TEST_P(DnsImplTest, DestructPending) {
   EXPECT_GT(peer_->events().size(), 0U);
 }
 
+// TODO(ClifHouck): With `ares_reinit()`, the TODO below is not accurate anymore.
 // Validate that All queries (2 concurrent queries) properly cleanup when the channel is destroyed.
 // TODO(mattklein123): This is a brute force way of testing this path, however we have seen
 // evidence that this happens during normal operation via the "channel dirty" path. The sequence
@@ -1036,7 +1037,7 @@ TEST_P(DnsImplTest, DestructCallback) {
 
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 // Validate basic success/fail lookup behavior. The "foo.bar.baz" request will connect
@@ -1076,19 +1077,19 @@ TEST_P(DnsImplTest, DnsIpAddressVersion) {
                                              {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::V4Only,
                                              DnsResolver::ResolutionStatus::Completed, {"1.2.3.4"},
                                              {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(3 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V6Only));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, DnsIpAddressVersionV6) {
@@ -1098,19 +1099,19 @@ TEST_P(DnsImplTest, DnsIpAddressVersionV6) {
                                              absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V4Only));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::V6Only,
                                              DnsResolver::ResolutionStatus::Completed, {"1::2"}, {},
                                              absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(3 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 // Validate exception behavior during c-ares callbacks.
@@ -1122,29 +1123,33 @@ TEST_P(DnsImplTest, CallbackException) {
   EXPECT_THROW_WITH_MESSAGE(dispatcher_->run(Event::Dispatcher::RunType::Block), EnvoyException,
                             "Envoy exception");
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_EQ(nullptr,
             resolveWithException<std::string>("1.2.3.4", DnsLookupFamily::V4Only, std::string()));
   EXPECT_THROW_WITH_MESSAGE(dispatcher_->run(Event::Dispatcher::RunType::Block), EnvoyException,
                             "unknown");
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
-// Verify that resetNetworking() correctly dirties and recreates the channel.
-TEST_P(DnsImplTest, DestroyChannelOnResetNetworking) {
-  ASSERT_FALSE(peer_->isChannelDirty());
+// Verify that resetNetworking() correctly reinitializes the channel.
+TEST_P(DnsImplTest, ReinitChannelOnResetNetworking) {
+  checkStats(0 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
+
   server_->addHosts("some.good.domain", {"201.134.56.7"}, RecordType::A);
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   resolver_->resetNetworking();
-  EXPECT_TRUE(peer_->isChannelDirty());
+  checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 1 /*reinitializations*/);
+
   resetChannel();
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
@@ -1152,16 +1157,19 @@ TEST_P(DnsImplTest, DestroyChannelOnResetNetworking) {
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 1 /*reinitializations*/);
 }
 
-// Validate that the c-ares channel is destroyed and re-initialized when c-ares returns
+// Validate that the c-ares channel is re-initialized when c-ares returns
 // ARES_ECONNREFUSED as its callback status.
-TEST_P(DnsImplTest, DestroyChannelOnRefused) {
+TEST_P(DnsImplTest, ReinitializeChannelOnRefused) {
   // See https://github.com/envoyproxy/envoy/issues/28504.
   DISABLE_UNDER_WINDOWS;
 
-  ASSERT_FALSE(peer_->isChannelDirty());
+  // Make sure there are no reinitializations yet.
+  checkStats(0 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
+
   server_->addHosts("some.good.domain", {"201.134.56.7"}, RecordType::A);
   server_->setRefused(true);
 
@@ -1169,40 +1177,30 @@ TEST_P(DnsImplTest, DestroyChannelOnRefused) {
             resolveWithExpectations("unresolvable.name", DnsLookupFamily::V4Only,
                                     DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
-  checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
-
-  // The c-ares channel should be dirty because the TestDnsServer replied with return code REFUSED;
-  // This test, and the way the TestDnsServerQuery is setup, relies on the fact that Envoy's
-  // c-ares channel is configured **without** the ARES_FLAG_NOCHECKRESP flag. This causes c-ares to
-  // discard packets with REFUSED, and thus Envoy receives ARES_ECONNREFUSED due to the code here:
+  // The c-ares channel should have reinitialized because the TestDnsServer replied with return code
+  // REFUSED; This test, and the way the TestDnsServerQuery is setup, relies on the fact that
+  // Envoy's c-ares channel is configured **without** the ARES_FLAG_NOCHECKRESP flag. This causes
+  // c-ares to discard packets with REFUSED, and thus Envoy receives ARES_ECONNREFUSED due to the
+  // code here:
   // https://github.com/c-ares/c-ares/blob/d7e070e7283f822b1d2787903cce3615536c5610/ares_process.c#L654
   // If that flag needs to be set, or c-ares changes its handling this test will need to be updated
   // to create another condition where c-ares invokes onAresGetAddrInfoCallback with status ==
   // ARES_ECONNREFUSED.
-  EXPECT_TRUE(peer_->isChannelDirty());
+  checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 1 /*reinitializations*/);
 
   server_->setRefused(false);
 
-  // Resolve will destroy the original channel and create a new one.
-  EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V4Only));
-  dispatcher_->run(Event::Dispatcher::RunType::Block);
-  // However, the fresh channel initialized by production code does not point to the TestDnsServer.
-  // This means that resolution will return `ARES_ENOTFOUND`. This should not dirty the channel.
-  EXPECT_FALSE(peer_->isChannelDirty());
-
-  // Reset the channel to point to the TestDnsServer, and make sure resolution is healthy.
-  resetChannel();
-  checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
-
-  EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
+  // This will succeed.
+  EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::V4Only,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
-  EXPECT_FALSE(peer_->isChannelDirty());
-  checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
+
+  // The reinitialized channel retains original settings. Therefore it should still be able
+  // to resolve correctly.
+  checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 1 /*reinitializations*/);
 }
 
 // Validate completed/fail lookup behavior via TestDnsServer. This exercises the
@@ -1213,14 +1211,14 @@ TEST_P(DnsImplTest, RemoteAsyncLookup) {
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.bad.domain", DnsLookupFamily::Auto));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 3 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 // Validate that multiple A records are correctly passed to the callback.
@@ -1233,7 +1231,7 @@ TEST_P(DnsImplTest, MultiARecordLookup) {
                                     {"201.134.56.7", "123.4.5.6", "6.5.4.3"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, CNameARecordLookupV4) {
@@ -1247,7 +1245,7 @@ TEST_P(DnsImplTest, CNameARecordLookupV4) {
                                              {"201.134.56.7"}, {}, std::chrono::seconds(60)));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, CNameARecordLookupWithV6) {
@@ -1261,7 +1259,7 @@ TEST_P(DnsImplTest, CNameARecordLookupWithV6) {
                                              {"201.134.56.7"}, {}, std::chrono::seconds(60)));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 // RFC 2181: TTL values can be between [0, 2^31-1]
@@ -1297,7 +1295,7 @@ TEST_P(DnsImplTest, CNameARecordLookupV4InvalidTTL) {
                                     std::chrono::seconds(2147483647)));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(3 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, MultiARecordLookupWithV6) {
@@ -1310,21 +1308,21 @@ TEST_P(DnsImplTest, MultiARecordLookupWithV6) {
                                     {"201.134.56.7", "123.4.5.6", "6.5.4.3"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {{"1::2", "1::2:3", "1::2:3:4"}}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::V6Only,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {{"1::2", "1::2:3", "1::2:3:4"}}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(3 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, AutoOnlyV6IfBothV6andV4) {
@@ -1336,7 +1334,7 @@ TEST_P(DnsImplTest, AutoOnlyV6IfBothV6andV4) {
                                              {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, AutoV6IfOnlyV6) {
@@ -1347,7 +1345,7 @@ TEST_P(DnsImplTest, AutoV6IfOnlyV6) {
                                              {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, AutoV4IfOnlyV4) {
@@ -1357,7 +1355,7 @@ TEST_P(DnsImplTest, AutoV4IfOnlyV4) {
                                              {{"201.134.56.7"}}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, V4PreferredOnlyV4IfBothV6andV4) {
@@ -1369,7 +1367,7 @@ TEST_P(DnsImplTest, V4PreferredOnlyV4IfBothV6andV4) {
                                              {{"201.134.56.7"}}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, V4PreferredV6IfOnlyV6) {
@@ -1380,7 +1378,7 @@ TEST_P(DnsImplTest, V4PreferredV6IfOnlyV6) {
                                              {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, V4PreferredV4IfOnlyV4) {
@@ -1390,7 +1388,7 @@ TEST_P(DnsImplTest, V4PreferredV4IfOnlyV4) {
                                              {{"201.134.56.7"}}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, AllIfBothV6andV4) {
@@ -1402,7 +1400,7 @@ TEST_P(DnsImplTest, AllIfBothV6andV4) {
                                              {{"201.134.56.7"}, {"1::2"}}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, AllV6IfOnlyV6) {
@@ -1413,7 +1411,7 @@ TEST_P(DnsImplTest, AllV6IfOnlyV6) {
                                              {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, AllV4IfOnlyV4) {
@@ -1423,7 +1421,7 @@ TEST_P(DnsImplTest, AllV4IfOnlyV4) {
                                              {{"201.134.56.7"}}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 // Validate working of cancellation provided by ActiveDnsQuery return.
@@ -1447,7 +1445,7 @@ TEST_P(DnsImplTest, Cancel) {
     dispatcher_->run(Event::Dispatcher::RunType::NonBlock);
   }
   checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 3 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 // Validate working of querying ttl of resource record.
@@ -1459,7 +1457,7 @@ TEST_P(DnsImplTest, RecordTtlLookup) {
                                                {"127.0.0.1"}, {}, std::chrono::seconds::zero()));
     dispatcher_->run(Event::Dispatcher::RunType::Block);
     checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-               0 /*get_addr_failure*/, 0 /*timeouts*/);
+               0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
     resolve_total++;
   }
 
@@ -1469,7 +1467,7 @@ TEST_P(DnsImplTest, RecordTtlLookup) {
                                                {}, std::chrono::seconds::zero()));
     dispatcher_->run(Event::Dispatcher::RunType::Block);
     checkStats(resolve_total + 1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-               0 /*get_addr_failure*/, 0 /*timeouts*/);
+               0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
     resolve_total++;
 
     EXPECT_EQ(nullptr, resolveWithExpectations("localhost", DnsLookupFamily::Auto,
@@ -1477,7 +1475,7 @@ TEST_P(DnsImplTest, RecordTtlLookup) {
                                                {}, std::chrono::seconds::zero()));
     dispatcher_->run(Event::Dispatcher::RunType::Block);
     checkStats(resolve_total + 1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-               0 /*get_addr_failure*/, 0 /*timeouts*/);
+               0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
     resolve_total++;
   }
 
@@ -1492,7 +1490,7 @@ TEST_P(DnsImplTest, RecordTtlLookup) {
                                     {"1::2", "1::2:3", "1::2:3:4"}, std::chrono::seconds(300)));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(resolve_total + 1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
   resolve_total++;
 
   EXPECT_NE(nullptr, resolveWithExpectations(
@@ -1501,7 +1499,7 @@ TEST_P(DnsImplTest, RecordTtlLookup) {
                          {"201.134.56.7", "123.4.5.6", "6.5.4.3"}, std::chrono::seconds(300)));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(resolve_total + 1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
   resolve_total++;
 
   EXPECT_NE(nullptr, resolveWithExpectations(
@@ -1510,7 +1508,7 @@ TEST_P(DnsImplTest, RecordTtlLookup) {
                          {"201.134.56.7", "123.4.5.6", "6.5.4.3"}, std::chrono::seconds(300)));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(resolve_total + 1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
   resolve_total++;
 
   server_->addHosts("domain.onion", {"1.2.3.4"}, RecordType::A);
@@ -1520,13 +1518,13 @@ TEST_P(DnsImplTest, RecordTtlLookup) {
   EXPECT_EQ(nullptr, resolveWithNoRecordsExpectation("domain.onion", DnsLookupFamily::V4Only));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(resolve_total + 1 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
   resolve_total++;
 
   EXPECT_EQ(nullptr, resolveWithNoRecordsExpectation("domain.onion.", DnsLookupFamily::V4Only));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(resolve_total + 1 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 // Validate that the resolution timeout timer is enabled if we don't resolve
@@ -1545,7 +1543,7 @@ TEST_P(DnsImplTest, PendingTimerEnable) {
   EXPECT_NE(nullptr, resolveWithUnreferencedParameters("some.bad.domain.invalid",
                                                        DnsLookupFamily::V4Only, true));
   checkStats(0 /*resolve_total*/, 1 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 // Validate that the pending resolutions stat is reset.
@@ -1556,38 +1554,38 @@ TEST_P(DnsImplTest, PendingResolutions) {
                                              {{"201.134.56.7"}}, {}, absl::nullopt));
 
   checkStats(0 /*resolve_total*/, 1 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, WithNoRecord) {
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V4Only));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V6Only));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::Auto));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 4 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr,
             resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V4Preferred));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(6 /*resolve_total*/, 0 /*pending_resolutions*/, 6 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::All));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(7 /*resolve_total*/, 0 /*pending_resolutions*/, 7 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, WithARecord) {
@@ -1597,33 +1595,33 @@ TEST_P(DnsImplTest, WithARecord) {
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V6Only));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::V4Preferred,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(5 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::All,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(6 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, WithAAAARecord) {
@@ -1631,35 +1629,35 @@ TEST_P(DnsImplTest, WithAAAARecord) {
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V4Only));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::V6Only,
                                              DnsResolver::ResolutionStatus::Completed, {"1::2"}, {},
                                              absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
                                              DnsResolver::ResolutionStatus::Completed, {"1::2"}, {},
                                              absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(3 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::V4Preferred,
                                              DnsResolver::ResolutionStatus::Completed, {"1::2"}, {},
                                              absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(5 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::All,
                                              DnsResolver::ResolutionStatus::Completed, {"1::2"}, {},
                                              absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(6 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, WithBothAAndAAAARecord) {
@@ -1670,35 +1668,35 @@ TEST_P(DnsImplTest, WithBothAAndAAAARecord) {
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::V6Only,
                                              DnsResolver::ResolutionStatus::Completed, {"1::2"}, {},
                                              absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
                                              DnsResolver::ResolutionStatus::Completed, {"1::2"}, {},
                                              absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(3 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::V4Preferred,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::All,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {"201.134.56.7", "1::2"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(5 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, FallbackToNodataWithErrorOnA) {
@@ -1708,28 +1706,28 @@ TEST_P(DnsImplTest, FallbackToNodataWithErrorOnA) {
                                     DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V6Only));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::Auto));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             2 /*get_addr_failure*/, 0 /*timeouts*/);
+             2 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr,
             resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V4Preferred));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(6 /*resolve_total*/, 0 /*pending_resolutions*/, 3 /*not_found*/,
-             3 /*get_addr_failure*/, 0 /*timeouts*/);
+             3 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::All));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(7 /*resolve_total*/, 0 /*pending_resolutions*/, 4 /*not_found*/,
-             3 /*get_addr_failure*/, 0 /*timeouts*/);
+             3 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, FallbackToNodataWithErrorOnAAAA) {
@@ -1739,23 +1737,23 @@ TEST_P(DnsImplTest, FallbackToNodataWithErrorOnAAAA) {
                                     DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V4Only));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::Auto));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             2 /*get_addr_failure*/, 0 /*timeouts*/);
+             2 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr,
             resolveWithNoRecordsExpectation("some.good.domain", DnsLookupFamily::V4Preferred));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(6 /*resolve_total*/, 0 /*pending_resolutions*/, 3 /*not_found*/,
-             3 /*get_addr_failure*/, 0 /*timeouts*/);
+             3 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   // When DnsLookupFamily::All is provided, both IPv4 and IPv6 queries are sent by c-ares in the
   // same ares_getaddrinfo operation. If one of them fails with `FORMERR`, the whole operation
@@ -1765,7 +1763,7 @@ TEST_P(DnsImplTest, FallbackToNodataWithErrorOnAAAA) {
                                     DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(7 /*resolve_total*/, 0 /*pending_resolutions*/, 3 /*not_found*/,
-             4 /*get_addr_failure*/, 0 /*timeouts*/);
+             4 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplTest, ErrorWithAcceptNodataEnabled) {
@@ -1776,35 +1774,35 @@ TEST_P(DnsImplTest, ErrorWithAcceptNodataEnabled) {
                                     DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr,
             resolveWithExpectations("some.good.domain", DnsLookupFamily::V6Only,
                                     DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             2 /*get_addr_failure*/, 0 /*timeouts*/);
+             2 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr,
             resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
                                     DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             4 /*get_addr_failure*/, 0 /*timeouts*/);
+             4 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr,
             resolveWithExpectations("some.good.domain", DnsLookupFamily::V4Preferred,
                                     DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(6 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             6 /*get_addr_failure*/, 0 /*timeouts*/);
+             6 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 
   EXPECT_NE(nullptr,
             resolveWithExpectations("some.good.domain", DnsLookupFamily::All,
                                     DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(7 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             7 /*get_addr_failure*/, 0 /*timeouts*/);
+             7 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 class DnsImplFilterUnroutableFamiliesTest : public DnsImplTest {
@@ -1819,27 +1817,27 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, DnsImplFilterUnroutableFamiliesTest,
 TEST_P(DnsImplFilterUnroutableFamiliesTest, FilterUnroutable) {
   testFilterAddresses({}, DnsLookupFamily::Auto, {}, DnsResolver::ResolutionStatus::Failure);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             2 /*get_addr_failure*/, 0 /*timeouts*/);
+             2 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplFilterUnroutableFamiliesTest, FilterUnroutableV6) {
   // Auto would have preferred V6, but because there is no v6 interface we will get v4.
   testFilterAddresses({"1.2.3.4:80"}, DnsLookupFamily::Auto, {"201.134.56.7"});
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplFilterUnroutableFamiliesTest, FilterUnroutableV6LoopbackDoesntCount) {
   testFilterAddresses({"1.2.3.4:80", "[::1]:80"}, DnsLookupFamily::Auto, {"201.134.56.7"});
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplFilterUnroutableFamiliesTest, DontFilterV6) {
   testFilterAddresses({"1.2.3.4:80", "[2001:0000:3238:DFE1:0063:0000:0000:FEFB]:54"},
                       DnsLookupFamily::Auto, {"1::2"});
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplFilterUnroutableFamiliesTest, FilterUnroutableV4) {
@@ -1847,48 +1845,48 @@ TEST_P(DnsImplFilterUnroutableFamiliesTest, FilterUnroutableV4) {
   testFilterAddresses({"[2001:0000:3238:DFE1:0063:0000:0000:FEFB]:54"},
                       DnsLookupFamily::V4Preferred, {"1::2"});
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplFilterUnroutableFamiliesTest, FilterUnroutableV4LoopbackDoesntCount) {
   testFilterAddresses({"[2001:0000:3238:DFE1:0063:0000:0000:FEFB]:54", "127.0.0.1:80"},
                       DnsLookupFamily::V4Preferred, {"1::2"});
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             1 /*get_addr_failure*/, 0 /*timeouts*/);
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplFilterUnroutableFamiliesTest, DontFilterV4) {
   testFilterAddresses({"1.2.3.4:80", "[2001:0000:3238:DFE1:0063:0000:0000:FEFB]:54"},
                       DnsLookupFamily::V4Preferred, {"201.134.56.7"});
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplFilterUnroutableFamiliesTest, DontFilterAll) {
   testFilterAddresses({"1.2.3.4:80", "[2001:0000:3238:DFE1:0063:0000:0000:FEFB]:54"},
                       DnsLookupFamily::All, {"201.134.56.7", "1::2"});
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplFilterUnroutableFamiliesTest, FilterAllV4) {
   testFilterAddresses({"[2001:0000:3238:DFE1:0063:0000:0000:FEFB]:54"}, DnsLookupFamily::All,
                       {"1::2"});
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplFilterUnroutableFamiliesTest, FilterAllV6) {
   testFilterAddresses({"1.2.3.4:80"}, DnsLookupFamily::All, {"201.134.56.7"});
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplFilterUnroutableFamiliesTest, DontFilterIfGetifaddrsIsNotSupported) {
   testFilterAddresses({}, DnsLookupFamily::All, {"201.134.56.7", "1::2"},
                       DnsResolver::ResolutionStatus::Completed, false /* getifaddrs_supported */);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 TEST_P(DnsImplFilterUnroutableFamiliesTest, DontFilterIfThereIsAGetifaddrsFailure) {
@@ -1896,7 +1894,7 @@ TEST_P(DnsImplFilterUnroutableFamiliesTest, DontFilterIfThereIsAGetifaddrsFailur
                       DnsResolver::ResolutionStatus::Completed, true /* getifaddrs_supported */,
                       false /* getifaddrs_success */);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 class DnsImplFilterUnroutableFamiliesDontFilterTest : public DnsImplTest {
@@ -1969,7 +1967,7 @@ TEST_P(DnsImplZeroTimeoutTest, Timeout) {
                                     DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 3 /*timeouts*/);
+             0 /*get_addr_failure*/, 3 /*timeouts*/, 0 /*reinitializations*/);
 }
 
 // Validate that c-ares query cache is disabled by default.
@@ -2098,8 +2096,10 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, DnsImplCustomResolverTest,
                          testing::ValuesIn(TestEnvironment::getIpVersionsForTest()),
                          TestUtility::ipTestParamsToString);
 
-TEST_P(DnsImplCustomResolverTest, CustomResolverValidAfterChannelDestruction) {
-  ASSERT_FALSE(peer_->isChannelDirty());
+TEST_P(DnsImplCustomResolverTest, CustomResolverValidAfterChannelReinitialization) {
+  checkStats(0 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinitializations*/);
+
   server_->addHosts("some.good.domain", {"201.134.56.7"}, RecordType::A);
   server_->setRefused(true);
 
@@ -2107,7 +2107,8 @@ TEST_P(DnsImplCustomResolverTest, CustomResolverValidAfterChannelDestruction) {
             resolveWithExpectations("some.good.domain", DnsLookupFamily::V4Only,
                                     DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
-  // The c-ares channel should be dirty because the TestDnsServer replied with return code REFUSED;
+  // The c-ares channel should have reinitialized because the TestDnsServer replied with
+  // return code REFUSED;
   // This test, and the way the TestDnsServerQuery is setup, relies on the fact that Envoy's
   // c-ares channel is configured **without** the ARES_FLAG_NOCHECKRESP flag. This causes c-ares to
   // discard packets with REFUSED, and thus Envoy receives ARES_ECONNREFUSED due to the code here:
@@ -2115,18 +2116,20 @@ TEST_P(DnsImplCustomResolverTest, CustomResolverValidAfterChannelDestruction) {
   // If that flag needs to be set, or c-ares changes its handling this test will need to be updated
   // to create another condition where c-ares invokes onAresGetAddrInfoCallback with status ==
   // ARES_ECONNREFUSED.
-  EXPECT_TRUE(peer_->isChannelDirty());
+  checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 1 /*reinitializations*/);
 
   server_->setRefused(false);
 
-  // The next query destroys, and re-initializes the channel. Furthermore, because the test dns
+  // Because the test dns
   // server's address was passed as a custom resolver on construction, the new channel should still
   // point to the test dns server, and the query should succeed.
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
-  EXPECT_FALSE(peer_->isChannelDirty());
+  checkStats(3 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
+             1 /*get_addr_failure*/, 0 /*timeouts*/, 1 /*reinitializations*/);
 }
 
 TEST_F(DnsImplConstructor, VerifyDefaultTimeoutAndTries) {
diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt
index a203fd5cf99bb..e3b18eef441a4 100644
--- a/tools/spelling/spelling_dictionary.txt
+++ b/tools/spelling/spelling_dictionary.txt
@@ -1215,6 +1215,10 @@ regexes
 reified
 reify
 reimplements
+reinitialization
+reinitializations
+reinitialize
+reinitializes
 rele
 releasor
 reloadable


Reviews:
user: login: agrawroh
body: Thanks for landing the prerequisites and making this change!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-25T15:25:49Z
user: login: ClifHouck
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T17:36:58Z
user: login: jmarantz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T14:01:19Z
user: login: ClifHouck
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T14:11:38Z
user: login: yanjunxiang-google
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T16:50:52Z
user: login: jmarantz
body: back to you Ryan for senior approval?

state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T17:19:02Z
user: login: RyanTheOptimist
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T17:45:25Z

Review comments:
diff_hunk: @@ -1122,87 +1123,86 @@ TEST_P(DnsImplTest, CallbackException) {
   EXPECT_THROW_WITH_MESSAGE(dispatcher_->run(Event::Dispatcher::RunType::Block), EnvoyException,
                             "Envoy exception");
   checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinits*/);
 
   EXPECT_EQ(nullptr,
             resolveWithException<std::string>("1.2.3.4", DnsLookupFamily::V4Only, std::string()));
   EXPECT_THROW_WITH_MESSAGE(dispatcher_->run(Event::Dispatcher::RunType::Block), EnvoyException,
                             "unknown");
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinits*/);
 }
 
-// Verify that resetNetworking() correctly dirties and recreates the channel.
-TEST_P(DnsImplTest, DestroyChannelOnResetNetworking) {
-  ASSERT_FALSE(peer_->isChannelDirty());
+// Verify that resetNetworking() correctly reinitializes the channel.
+TEST_P(DnsImplTest, ReinitChannelOnResetNetworking) {
+  checkStats(0 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/,
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinits*/);
+
   server_->addHosts("some.good.domain", {"201.134.56.7"}, RecordType::A);
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 0 /*reinits*/);
 
   resolver_->resetNetworking();
-  EXPECT_TRUE(peer_->isChannelDirty());
+  checkStats(2 /*resolve_total*/, 0 /*pending_resolutions*/, 1 /*not_found*/,
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 1 /*reinits*/);
+
   resetChannel();
 
   EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::Auto,
                                              DnsResolver::ResolutionStatus::Completed,
                                              {"201.134.56.7"}, {}, absl::nullopt));
   dispatcher_->run(Event::Dispatcher::RunType::Block);
   checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 2 /*not_found*/,
-             0 /*get_addr_failure*/, 0 /*timeouts*/);
+             0 /*get_addr_failure*/, 0 /*timeouts*/, 1 /*reinits*/);
 }
 
-// Validate that the c-ares channel is destroyed and re-initialized when c-ares returns
+// Validate that the c-ares channel is re-initialized when c-ares returns
 // ARES_ECONNREFUSED as its callback status.
-TEST_P(DnsImplTest, DestroyChannelOnRefused) {
+TEST_P(DnsImplTest, ReinitializeChannelOnRefused) {
+  Envoy::Logger::Registry::setLogLevel(spdlog::level::debug);
path: test/extensions/network/dns_resolver/cares/dns_impl_test.cc
user: login: ClifHouck
body: Need to remove this.
created_at: 2025-03-25T17:36:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1213,6 +1214,10 @@ regexes
 reified
 reify
 reimplements
+reinit
path: tools/spelling/spelling_dictionary.txt
user: login: jmarantz
body: reinit and reinits are not really words. Where are we failing?

Can we fix the source instead of whitelisting these non-words?
created_at: 2025-03-26T00:04:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1213,6 +1214,10 @@ regexes
 reified
 reify
 reimplements
+reinit
path: tools/spelling/spelling_dictionary.txt
user: login: ClifHouck
body: Sure, I'll correct this.
created_at: 2025-03-26T14:11:38Z
author_association: CONTRIBUTOR

PR comments:
user: login: ClifHouck
created_at: 2025-03-25T01:10:05Z
body: @RyanTheOptimist Thanks for your review on the c-ares upgrade PR. This is the follow-up to actually use `ares_reinit`.

@agrawroh This commit adapts most of your work to implement the use of `ares_reinit` on top of my recent changes. Hopefully you don't mind.
author_association: CONTRIBUTOR
user: login: ClifHouck
created_at: 2025-03-25T14:17:50Z
body: /retest
author_association: CONTRIBUTOR
user: login: ClifHouck
created_at: 2025-03-25T14:19:59Z
body: Is there a straightforward way to run arm tests locally? I'm on amd64.
author_association: CONTRIBUTOR
user: login: ClifHouck
created_at: 2025-03-25T14:34:39Z
body: Oh nice, looks like it was a flake after all.
author_association: CONTRIBUTOR
user: login: ClifHouck
created_at: 2025-03-25T21:56:00Z
body: /retest
author_association: CONTRIBUTOR
user: login: jmarantz
created_at: 2025-03-25T21:57:23Z
body: Just a quick note: c_ares init/destruct is thread-hostile. Be very careful about how you use it.

Using it in test-main should be fine.

https://github.com/c-ares/c-ares/issues/475

A lot has happened in c_ares since our interaction with them back in 2022, so this thread-hostile property may be dated.
author_association: CONTRIBUTOR
user: login: RyanTheOptimist
created_at: 2025-03-25T22:45:45Z
body: I think this looks good to me, but I'm not very familiar with c-ares internals. @jmarantz could I impose upon you to review this?
author_association: CONTRIBUTOR
user: login: jmarantz
created_at: 2025-03-26T00:06:33Z
body: What was the flake?

When I heard "flake" and "c_ares" together I was reminded of the project's position on not fixing their init thread-safety issue., but this re-initilaization of a channel seems possibly different and OK.

However @yanjunxiang-google is the expert afaik so I'll defer to him.
author_association: CONTRIBUTOR
user: login: ClifHouck
created_at: 2025-03-26T13:52:49Z
body: > What was the flake?
> 
> When I heard "flake" and "c_ares" together I was reminded of the project's position on not fixing their init thread-safety issue., but this re-initilaization of a channel seems possibly different and OK.
> 
> However @yanjunxiang-google is the expert afaik so I'll defer to him.

Test flake, as far as I can tell the tests that failed were completely unrelated to c-ares. I enabled debug logging on one locally and no calls to c-ares were made.
author_association: CONTRIBUTOR
user: login: jmarantz
created_at: 2025-03-26T14:01:06Z
body: Sounds good to me. Yanjun can you take a pass then I'll approve?

author_association: CONTRIBUTOR
user: login: ClifHouck
created_at: 2025-03-26T14:10:48Z
body: Based on the [documentation](https://c-ares.org/docs/ares_reinit.html) for `ares_reinit` it appears that it is designed with some thread-safety in mind:

> [ares_reinit](https://c-ares.org/docs/ares_reinit.html), when compiled with thread safety, will spawn a background thread to read the configuration and apply it. It is crucial that developers use the ARES_OPT_SOCK_STATE_CB or ARES_OPT_EVENT_THREAD so that notifications of changes are alerted. If using [ares_getsock](https://c-ares.org/docs/ares_getsock.html) or [ares_fds](https://c-ares.org/docs/ares_fds.html), no notification is possible which could cause a stall.

So it seems there are two conditions for thread safety:

1. Compile c-ares with thread safety.
2. Use `ARES_OPT_SOCK_STATE_CB` or `ARES_OPT_EVENT_THREAD` as an option when calling `ares_init{_options}`.

For 1: Envoy uses `ARES_OPT_SOCK_STATE_CB` [currently](https://github.com/envoyproxy/envoy/blob/main/source/extensions/network/dns_resolver/cares/dns_impl.cc#L158). For 2: The `CMakeLists.txt` that currently ships with c-ares has [thread safety on by default](https://github.com/c-ares/c-ares/blob/main/CMakeLists.txt#L44).

Additionally the docs state that in-flight queries will be requeued: 

> Any existing queries will be automatically requeued if the server they are currently assigned to is removed from the system configuration. 

Based on the above, I believe the way this PR is currently calling `ares_reinit` is likely OK. At least it satisfies the requirements laid out in the documentation. @agrawroh has also stated that this implementation of `ares_reinit` has been deployed in their environment and appears to function properly.
author_association: CONTRIBUTOR
user: login: yanjunxiang-google
created_at: 2025-03-26T16:44:27Z
body: > What was the flake?
> 
> When I heard "flake" and "c_ares" together I was reminded of the project's position on not fixing their init thread-safety issue., but this re-initilaization of a channel seems possibly different and OK.
> 
> However @yanjunxiang-google is the expert afaik so I'll defer to him.

It looks to me this change is orthogonal to the existing c-ares thread-safety issue.   @ClifHouck the CI failed at QUIC test, do you need to merge upstream?
author_association: CONTRIBUTOR
user: login: ClifHouck
created_at: 2025-03-26T16:47:04Z
body: I'll try re-basing on latest `main`
author_association: CONTRIBUTOR
user: login: yanjunxiang-google
created_at: 2025-03-26T16:50:36Z
body: LGTM
author_association: CONTRIBUTOR
user: login: ClifHouck
created_at: 2025-03-26T17:49:06Z
body: Thanks everyone for the reviews and getting the right people looking at this! :rocket: 
author_association: CONTRIBUTOR