LCOV - code coverage report
Current view: top level - source/extensions/network/dns_resolver/cares - dns_impl.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 9 397 2.3 %
Date: 2024-01-05 06:35:25 Functions: 3 32 9.4 %

          Line data    Source code
       1             : #include "source/extensions/network/dns_resolver/cares/dns_impl.h"
       2             : 
       3             : #include <chrono>
       4             : #include <cstdint>
       5             : #include <list>
       6             : #include <memory>
       7             : #include <string>
       8             : 
       9             : #include "envoy/common/platform.h"
      10             : #include "envoy/registry/registry.h"
      11             : 
      12             : #include "source/common/api/os_sys_calls_impl.h"
      13             : #include "source/common/common/assert.h"
      14             : #include "source/common/common/fmt.h"
      15             : #include "source/common/common/thread.h"
      16             : #include "source/common/network/address_impl.h"
      17             : #include "source/common/network/resolver_impl.h"
      18             : #include "source/common/network/utility.h"
      19             : #include "source/common/runtime/runtime_features.h"
      20             : 
      21             : #include "absl/strings/str_join.h"
      22             : #include "ares.h"
      23             : 
      24             : namespace Envoy {
      25             : namespace Network {
      26             : 
      27             : DnsResolverImpl::DnsResolverImpl(
      28             :     const envoy::extensions::network::dns_resolver::cares::v3::CaresDnsResolverConfig& config,
      29             :     Event::Dispatcher& dispatcher,
      30             :     const std::vector<Network::Address::InstanceConstSharedPtr>& resolvers,
      31             :     Stats::Scope& root_scope)
      32             :     : dispatcher_(dispatcher),
      33           0 :       timer_(dispatcher.createTimer([this] { onEventCallback(ARES_SOCKET_BAD, 0); })),
      34             :       dns_resolver_options_(config.dns_resolver_options()),
      35             :       use_resolvers_as_fallback_(config.use_resolvers_as_fallback()),
      36             :       resolvers_csv_(maybeBuildResolversCsv(resolvers)),
      37             :       filter_unroutable_families_(config.filter_unroutable_families()),
      38           0 :       scope_(root_scope.createScope("dns.cares.")), stats_(generateCaresDnsResolverStats(*scope_)) {
      39           0 :   AresOptions options = defaultAresOptions();
      40           0 :   initializeChannel(&options.options_, options.optmask_);
      41           0 : }
      42             : 
      43           0 : DnsResolverImpl::~DnsResolverImpl() {
      44           0 :   timer_->disableTimer();
      45           0 :   ares_destroy(channel_);
      46           0 : }
      47             : 
      48           0 : CaresDnsResolverStats DnsResolverImpl::generateCaresDnsResolverStats(Stats::Scope& scope) {
      49           0 :   return {ALL_CARES_DNS_RESOLVER_STATS(POOL_COUNTER(scope), POOL_GAUGE(scope))};
      50           0 : }
      51             : 
      52             : absl::optional<std::string> DnsResolverImpl::maybeBuildResolversCsv(
      53           0 :     const std::vector<Network::Address::InstanceConstSharedPtr>& resolvers) {
      54           0 :   if (resolvers.empty()) {
      55           0 :     return absl::nullopt;
      56           0 :   }
      57             : 
      58           0 :   std::vector<std::string> resolver_addrs;
      59           0 :   resolver_addrs.reserve(resolvers.size());
      60           0 :   for (const auto& resolver : resolvers) {
      61             :     // This should be an IP address (i.e. not a pipe).
      62           0 :     if (resolver->ip() == nullptr) {
      63           0 :       throw EnvoyException(
      64           0 :           fmt::format("DNS resolver '{}' is not an IP address", resolver->asString()));
      65           0 :     }
      66             :     // Note that the ip()->port() may be zero if the port is not fully specified by the
      67             :     // Address::Instance.
      68             :     // resolver->asString() is avoided as that format may be modified by custom
      69             :     // Address::Instance implementations in ways that make the <port> not a simple
      70             :     // integer. See https://github.com/envoyproxy/envoy/pull/3366.
      71           0 :     resolver_addrs.push_back(fmt::format(fmt::runtime(resolver->ip()->ipv6() ? "[{}]:{}" : "{}:{}"),
      72           0 :                                          resolver->ip()->addressAsString(),
      73           0 :                                          resolver->ip()->port()));
      74           0 :   }
      75           0 :   return {absl::StrJoin(resolver_addrs, ",")};
      76           0 : }
      77             : 
      78           0 : DnsResolverImpl::AresOptions DnsResolverImpl::defaultAresOptions() {
      79           0 :   AresOptions options{};
      80             : 
      81           0 :   if (dns_resolver_options_.use_tcp_for_dns_lookups()) {
      82           0 :     options.optmask_ |= ARES_OPT_FLAGS;
      83           0 :     options.options_.flags |= ARES_FLAG_USEVC;
      84           0 :   }
      85             : 
      86           0 :   if (dns_resolver_options_.no_default_search_domain()) {
      87           0 :     options.optmask_ |= ARES_OPT_FLAGS;
      88           0 :     options.options_.flags |= ARES_FLAG_NOSEARCH;
      89           0 :   }
      90             : 
      91           0 :   return options;
      92           0 : }
      93             : 
      94           0 : bool DnsResolverImpl::isCaresDefaultTheOnlyNameserver() {
      95           0 :   struct ares_addr_port_node* servers{};
      96           0 :   int result = ares_get_servers_ports(channel_, &servers);
      97           0 :   RELEASE_ASSERT(result == ARES_SUCCESS, "failure in ares_get_servers_ports");
      98             :   // as determined in init_by_defaults in ares_init.c.
      99           0 :   const bool has_only_default_nameserver =
     100           0 :       servers == nullptr || (servers->next == nullptr && servers->family == AF_INET &&
     101           0 :                              servers->addr.addr4.s_addr == htonl(INADDR_LOOPBACK) &&
     102           0 :                              servers->udp_port == 0 && servers->tcp_port == 0);
     103           0 :   if (servers != nullptr) {
     104           0 :     ares_free_data(servers);
     105           0 :   }
     106           0 :   return has_only_default_nameserver;
     107           0 : }
     108             : 
     109           0 : void DnsResolverImpl::initializeChannel(ares_options* options, int optmask) {
     110           0 :   dirty_channel_ = false;
     111             : 
     112           0 :   options->sock_state_cb = [](void* arg, os_fd_t fd, int read, int write) {
     113           0 :     static_cast<DnsResolverImpl*>(arg)->onAresSocketStateChange(fd, read, write);
     114           0 :   };
     115           0 :   options->sock_state_cb_data = this;
     116           0 :   ares_init_options(&channel_, options, optmask | ARES_OPT_SOCK_STATE_CB);
     117             : 
     118           0 :   if (resolvers_csv_.has_value()) {
     119           0 :     bool use_resolvers = true;
     120             :     // If the only name server available is c-ares' default then fallback to the user defined
     121             :     // resolvers. Otherwise, use the resolvers provided by c-ares.
     122           0 :     if (use_resolvers_as_fallback_ && !isCaresDefaultTheOnlyNameserver()) {
     123           0 :       use_resolvers = false;
     124           0 :     }
     125             : 
     126           0 :     if (use_resolvers) {
     127           0 :       int result = ares_set_servers_ports_csv(channel_, resolvers_csv_->c_str());
     128           0 :       RELEASE_ASSERT(result == ARES_SUCCESS, "");
     129           0 :     }
     130           0 :   }
     131           0 : }
     132             : 
     133             : // Treat responses with `ARES_ENODATA` or `ARES_ENOTFOUND` status as DNS response with no records.
     134             : // @see DnsResolverImpl::PendingResolution::onAresGetAddrInfoCallback for details.
     135           0 : bool DnsResolverImpl::AddrInfoPendingResolution::isResponseWithNoRecords(int status) {
     136           0 :   return status == ARES_ENODATA || status == ARES_ENOTFOUND;
     137           0 : }
     138             : 
     139             : void DnsResolverImpl::AddrInfoPendingResolution::onAresGetAddrInfoCallback(
     140           0 :     int status, int timeouts, ares_addrinfo* addrinfo) {
     141           0 :   ASSERT(pending_resolutions_ > 0);
     142           0 :   pending_resolutions_--;
     143             : 
     144           0 :   parent_.stats_.resolve_total_.inc();
     145           0 :   parent_.stats_.pending_resolutions_.dec();
     146             : 
     147           0 :   if (status != ARES_SUCCESS) {
     148           0 :     parent_.chargeGetAddrInfoErrorStats(status, timeouts);
     149             : 
     150           0 :     if (!isResponseWithNoRecords(status)) {
     151           0 :       ENVOY_LOG_EVENT(debug, "cares_resolution_failure",
     152           0 :                       "dns resolution for {} failed with c-ares status {}", dns_name_, status);
     153           0 :     } else {
     154           0 :       ENVOY_LOG_EVENT(debug, "cares_resolution_no_records", "dns resolution without records for {}",
     155           0 :                       dns_name_);
     156           0 :     }
     157           0 :   }
     158             : 
     159             :   // We receive ARES_EDESTRUCTION when destructing with pending queries.
     160           0 :   if (status == ARES_EDESTRUCTION) {
     161             :     // In the destruction path we must wait until there are no more pending queries. Resolution is
     162             :     // not truly finished until the last parallel query has been destroyed.
     163           0 :     if (pending_resolutions_ > 0) {
     164           0 :       return;
     165           0 :     }
     166             : 
     167           0 :     ASSERT(owned_);
     168             :     // This destruction might have been triggered by a peer PendingResolution that received a
     169             :     // ARES_ECONNREFUSED. If the PendingResolution has not been cancelled that means that the
     170             :     // callback_ target _should_ still be around. In that case, raise the callback_ so the target
     171             :     // can be done with this query and initiate a new one.
     172           0 :     ENVOY_LOG_EVENT(debug, "cares_dns_resolution_destroyed", "dns resolution for {} destroyed",
     173           0 :                     dns_name_);
     174             : 
     175             :     // Nothing can follow a call to finishResolve due to the deletion of this object upon
     176             :     // finishResolve().
     177           0 :     finishResolve();
     178           0 :     return;
     179           0 :   }
     180             : 
     181           0 :   if (!dual_resolution_) {
     182           0 :     completed_ = true;
     183             : 
     184             :     // If c-ares returns ARES_ECONNREFUSED and there is no fallback we assume that the channel_ is
     185             :     // broken. Mark the channel dirty so that it is destroyed and reinitialized on a subsequent call
     186             :     // to DnsResolver::resolve(). The optimal solution would be for c-ares to reinitialize the
     187             :     // channel, and not have Envoy track side effects.
     188             :     // context: https://github.com/envoyproxy/envoy/issues/4543 and
     189             :     // https://github.com/c-ares/c-ares/issues/301.
     190             :     //
     191             :     // The channel cannot be destroyed and reinitialized here because that leads to a c-ares
     192             :     // segfault.
     193           0 :     if (status == ARES_ECONNREFUSED) {
     194           0 :       parent_.dirty_channel_ = true;
     195           0 :     }
     196           0 :   }
     197             : 
     198           0 :   if (status == ARES_SUCCESS) {
     199           0 :     pending_response_.status_ = ResolutionStatus::Success;
     200             : 
     201           0 :     if (addrinfo != nullptr && addrinfo->nodes != nullptr) {
     202           0 :       bool can_process_v4 =
     203           0 :           (!parent_.filter_unroutable_families_ || available_interfaces_.v4_available_);
     204           0 :       bool can_process_v6 =
     205           0 :           (!parent_.filter_unroutable_families_ || available_interfaces_.v6_available_);
     206             : 
     207           0 :       int min_ttl = INT_MAX; // [RFC 2181](https://datatracker.ietf.org/doc/html/rfc2181)
     208             :       // Loop through CNAME and get min_ttl
     209           0 :       for (const ares_addrinfo_cname* cname = addrinfo->cnames; cname != nullptr;
     210           0 :            cname = cname->next) {
     211           0 :         min_ttl = std::min(min_ttl, cname->ttl);
     212           0 :       }
     213             : 
     214           0 :       for (const ares_addrinfo_node* ai = addrinfo->nodes; ai != nullptr; ai = ai->ai_next) {
     215           0 :         if (ai->ai_family == AF_INET && can_process_v4) {
     216           0 :           sockaddr_in address;
     217           0 :           memset(&address, 0, sizeof(address));
     218           0 :           address.sin_family = AF_INET;
     219           0 :           address.sin_port = 0;
     220           0 :           address.sin_addr = reinterpret_cast<sockaddr_in*>(ai->ai_addr)->sin_addr;
     221             : 
     222           0 :           pending_response_.address_list_.emplace_back(
     223           0 :               DnsResponse(std::make_shared<const Address::Ipv4Instance>(&address),
     224           0 :                           std::chrono::seconds(std::min(min_ttl, ai->ai_ttl))));
     225           0 :         } else if (ai->ai_family == AF_INET6 && can_process_v6) {
     226           0 :           sockaddr_in6 address;
     227           0 :           memset(&address, 0, sizeof(address));
     228           0 :           address.sin6_family = AF_INET6;
     229           0 :           address.sin6_port = 0;
     230           0 :           address.sin6_addr = reinterpret_cast<sockaddr_in6*>(ai->ai_addr)->sin6_addr;
     231           0 :           pending_response_.address_list_.emplace_back(
     232           0 :               DnsResponse(std::make_shared<const Address::Ipv6Instance>(address),
     233           0 :                           std::chrono::seconds(std::min(min_ttl, ai->ai_ttl))));
     234           0 :         }
     235           0 :       }
     236           0 :     }
     237             : 
     238           0 :     if (!pending_response_.address_list_.empty() && dns_lookup_family_ != DnsLookupFamily::All) {
     239           0 :       completed_ = true;
     240           0 :     }
     241             : 
     242           0 :     ASSERT(addrinfo != nullptr);
     243           0 :     ares_freeaddrinfo(addrinfo);
     244           0 :   } else if (isResponseWithNoRecords(status)) {
     245             :     // Treat `ARES_ENODATA` or `ARES_ENOTFOUND` here as success to populate back the
     246             :     // "empty records" response.
     247           0 :     pending_response_.status_ = ResolutionStatus::Success;
     248           0 :     ASSERT(addrinfo == nullptr);
     249           0 :   }
     250             : 
     251           0 :   if (timeouts > 0) {
     252           0 :     ENVOY_LOG(debug, "DNS request timed out {} times", timeouts);
     253           0 :   }
     254             : 
     255           0 :   if (completed_) {
     256           0 :     finishResolve();
     257             :     // Nothing can follow a call to finishResolve due to the deletion of this object upon
     258             :     // finishResolve().
     259           0 :     return;
     260           0 :   }
     261             : 
     262           0 :   if (dual_resolution_) {
     263           0 :     dual_resolution_ = false;
     264             : 
     265             :     // Perform a second lookup for DnsLookupFamily::Auto and DnsLookupFamily::V4Preferred, given
     266             :     // that the first lookup failed to return any addresses. Note that DnsLookupFamily::All issues
     267             :     // both lookups concurrently so there is no need to fire a second lookup here.
     268           0 :     if (dns_lookup_family_ == DnsLookupFamily::Auto) {
     269           0 :       family_ = AF_INET;
     270           0 :       startResolutionImpl(AF_INET);
     271           0 :     } else if (dns_lookup_family_ == DnsLookupFamily::V4Preferred) {
     272           0 :       family_ = AF_INET6;
     273           0 :       startResolutionImpl(AF_INET6);
     274           0 :     }
     275             : 
     276             :     // Note: Nothing can follow this call to getAddrInfo due to deletion of this
     277             :     // object upon synchronous resolution.
     278           0 :     return;
     279           0 :   }
     280           0 : }
     281             : 
     282           0 : void DnsResolverImpl::PendingResolution::finishResolve() {
     283           0 :   ENVOY_LOG_EVENT(debug, "cares_dns_resolution_complete",
     284           0 :                   "dns resolution for {} completed with status {}", dns_name_,
     285           0 :                   static_cast<int>(pending_response_.status_));
     286             : 
     287           0 :   if (!cancelled_) {
     288             :     // Use a raw try here because it is used in both main thread and filter.
     289             :     // Can not convert to use status code as there may be unexpected exceptions in server fuzz
     290             :     // tests, which must be handled. Potential exception may come from getAddressWithPort() or
     291             :     // portFromTcpUrl().
     292             :     // TODO(chaoqin-li1123): remove try catch pattern here once we figure how to handle unexpected
     293             :     // exception in fuzz tests.
     294           0 :     TRY_NEEDS_AUDIT {
     295           0 :       callback_(pending_response_.status_, std::move(pending_response_.address_list_));
     296           0 :     }
     297           0 :     END_TRY
     298           0 :     catch (const EnvoyException& e) {
     299           0 :       ENVOY_LOG(critical, "EnvoyException in c-ares callback: {}", e.what());
     300           0 :       dispatcher_.post([s = std::string(e.what())] { throw EnvoyException(s); });
     301           0 :     }
     302           0 :     catch (const std::exception& e) {
     303           0 :       ENVOY_LOG(critical, "std::exception in c-ares callback: {}", e.what());
     304           0 :       dispatcher_.post([s = std::string(e.what())] { throw EnvoyException(s); });
     305           0 :     }
     306           0 :     catch (...) {
     307           0 :       ENVOY_LOG(critical, "Unknown exception in c-ares callback");
     308           0 :       dispatcher_.post([] { throw EnvoyException("unknown"); });
     309           0 :     }
     310           0 :   } else {
     311           0 :     ENVOY_LOG_EVENT(debug, "cares_dns_callback_cancelled",
     312           0 :                     "dns resolution callback for {} not issued. Cancelled with reason={}",
     313           0 :                     dns_name_, static_cast<int>(cancel_reason_));
     314           0 :   }
     315           0 :   if (owned_) {
     316           0 :     delete this;
     317           0 :     return;
     318           0 :   }
     319           0 : }
     320             : 
     321           0 : void DnsResolverImpl::updateAresTimer() {
     322             :   // Update the timeout for events.
     323           0 :   timeval timeout;
     324           0 :   timeval* timeout_result = ares_timeout(channel_, nullptr, &timeout);
     325           0 :   if (timeout_result != nullptr) {
     326           0 :     const auto ms =
     327           0 :         std::chrono::milliseconds(timeout_result->tv_sec * 1000 + timeout_result->tv_usec / 1000);
     328           0 :     ENVOY_LOG(trace, "Setting DNS resolution timer for {} milliseconds", ms.count());
     329           0 :     timer_->enableTimer(ms);
     330           0 :   } else {
     331           0 :     timer_->disableTimer();
     332           0 :   }
     333           0 : }
     334             : 
     335           0 : void DnsResolverImpl::onEventCallback(os_fd_t fd, uint32_t events) {
     336           0 :   const ares_socket_t read_fd = events & Event::FileReadyType::Read ? fd : ARES_SOCKET_BAD;
     337           0 :   const ares_socket_t write_fd = events & Event::FileReadyType::Write ? fd : ARES_SOCKET_BAD;
     338           0 :   ares_process_fd(channel_, read_fd, write_fd);
     339           0 :   updateAresTimer();
     340           0 : }
     341             : 
     342           0 : void DnsResolverImpl::onAresSocketStateChange(os_fd_t fd, int read, int write) {
     343           0 :   updateAresTimer();
     344           0 :   auto it = events_.find(fd);
     345             :   // Stop tracking events for fd if no more state change events.
     346           0 :   if (read == 0 && write == 0) {
     347           0 :     if (it != events_.end()) {
     348           0 :       events_.erase(it);
     349           0 :     }
     350           0 :     return;
     351           0 :   }
     352             : 
     353             :   // If we weren't tracking the fd before, create a new FileEvent.
     354           0 :   if (it == events_.end()) {
     355           0 :     events_[fd] = dispatcher_.createFileEvent(
     356           0 :         fd, [this, fd](uint32_t events) { onEventCallback(fd, events); },
     357           0 :         Event::FileTriggerType::Level, Event::FileReadyType::Read | Event::FileReadyType::Write);
     358           0 :   }
     359           0 :   events_[fd]->setEnabled((read ? Event::FileReadyType::Read : 0) |
     360           0 :                           (write ? Event::FileReadyType::Write : 0));
     361           0 : }
     362             : 
     363             : ActiveDnsQuery* DnsResolverImpl::resolve(const std::string& dns_name,
     364           0 :                                          DnsLookupFamily dns_lookup_family, ResolveCb callback) {
     365           0 :   ENVOY_LOG_EVENT(debug, "cares_dns_resolution_start", "dns resolution for {} started", dns_name);
     366             : 
     367             :   // TODO(hennna): Add DNS caching which will allow testing the edge case of a
     368             :   // failed initial call to getAddrInfo followed by a synchronous IPv4
     369             :   // resolution.
     370             : 
     371             :   // @see DnsResolverImpl::PendingResolution::onAresGetAddrInfoCallback for why this is done.
     372           0 :   if (dirty_channel_) {
     373           0 :     ares_destroy(channel_);
     374           0 :     AresOptions options = defaultAresOptions();
     375           0 :     initializeChannel(&options.options_, options.optmask_);
     376           0 :   }
     377             : 
     378           0 :   auto pending_resolution = std::make_unique<AddrInfoPendingResolution>(
     379           0 :       *this, callback, dispatcher_, channel_, dns_name, dns_lookup_family);
     380           0 :   pending_resolution->startResolution();
     381           0 :   if (pending_resolution->completed_) {
     382             :     // Resolution does not need asynchronous behavior or network events. For
     383             :     // example, localhost lookup.
     384           0 :     return nullptr;
     385           0 :   } else {
     386             :     // Enable timer to wake us up if the request times out.
     387           0 :     updateAresTimer();
     388             : 
     389             :     // The PendingResolution will self-delete when the request completes (including if cancelled or
     390             :     // if ~DnsResolverImpl() happens via ares_destroy() and subsequent handling of ARES_EDESTRUCTION
     391             :     // in DnsResolverImpl::PendingResolution::onAresGetAddrInfoCallback()).
     392           0 :     pending_resolution->owned_ = true;
     393           0 :     return pending_resolution.release();
     394           0 :   }
     395           0 : }
     396             : 
     397           0 : void DnsResolverImpl::chargeGetAddrInfoErrorStats(int status, int timeouts) {
     398           0 :   switch (status) {
     399           0 :   case ARES_ENODATA:
     400           0 :     ABSL_FALLTHROUGH_INTENDED;
     401           0 :   case ARES_ENOTFOUND:
     402           0 :     stats_.not_found_.inc();
     403           0 :     break;
     404           0 :   case ARES_ETIMEOUT:
     405           0 :     stats_.timeouts_.add(timeouts);
     406           0 :     break;
     407           0 :   default:
     408           0 :     stats_.get_addr_failure_.inc();
     409           0 :   }
     410           0 : }
     411             : 
     412             : DnsResolverImpl::AddrInfoPendingResolution::AddrInfoPendingResolution(
     413             :     DnsResolverImpl& parent, ResolveCb callback, Event::Dispatcher& dispatcher,
     414             :     ares_channel channel, const std::string& dns_name, DnsLookupFamily dns_lookup_family)
     415             :     : PendingResolution(parent, callback, dispatcher, channel, dns_name),
     416           0 :       dns_lookup_family_(dns_lookup_family), available_interfaces_(availableInterfaces()) {
     417           0 :   if (dns_lookup_family == DnsLookupFamily::Auto ||
     418           0 :       dns_lookup_family == DnsLookupFamily::V4Preferred) {
     419           0 :     dual_resolution_ = true;
     420           0 :   }
     421             : 
     422           0 :   switch (dns_lookup_family_) {
     423           0 :   case DnsLookupFamily::V4Only:
     424           0 :   case DnsLookupFamily::V4Preferred:
     425           0 :     family_ = AF_INET;
     426           0 :     break;
     427           0 :   case DnsLookupFamily::V6Only:
     428           0 :   case DnsLookupFamily::Auto:
     429           0 :     family_ = AF_INET6;
     430           0 :     break;
     431           0 :   case DnsLookupFamily::All:
     432           0 :     family_ = AF_UNSPEC;
     433           0 :     break;
     434           0 :   }
     435           0 : }
     436             : 
     437           0 : DnsResolverImpl::AddrInfoPendingResolution::~AddrInfoPendingResolution() {
     438             :   // All pending resolutions should be cleaned up at this point.
     439           0 :   ASSERT(pending_resolutions_ == 0);
     440           0 : }
     441             : 
     442           0 : void DnsResolverImpl::AddrInfoPendingResolution::startResolution() { startResolutionImpl(family_); }
     443             : 
     444           0 : void DnsResolverImpl::AddrInfoPendingResolution::startResolutionImpl(int family) {
     445           0 :   pending_resolutions_++;
     446           0 :   parent_.stats_.pending_resolutions_.inc();
     447           0 :   if (parent_.filter_unroutable_families_ && family != AF_UNSPEC) {
     448           0 :     switch (family) {
     449           0 :     case AF_INET:
     450           0 :       if (!available_interfaces_.v4_available_) {
     451           0 :         ENVOY_LOG_EVENT(debug, "cares_resolution_filtered", "filtered v4 lookup");
     452           0 :         onAresGetAddrInfoCallback(ARES_EBADFAMILY, 0, nullptr);
     453           0 :         return;
     454           0 :       }
     455           0 :       break;
     456           0 :     case AF_INET6:
     457           0 :       if (!available_interfaces_.v6_available_) {
     458           0 :         ENVOY_LOG_EVENT(debug, "cares_resolution_filtered", "filtered v6 lookup");
     459           0 :         onAresGetAddrInfoCallback(ARES_EBADFAMILY, 0, nullptr);
     460           0 :         return;
     461           0 :       }
     462           0 :       break;
     463           0 :     default:
     464           0 :       ENVOY_BUG(false, fmt::format("Unexpected IP family {}", family));
     465           0 :     }
     466           0 :   }
     467             : 
     468           0 :   struct ares_addrinfo_hints hints = {};
     469           0 :   hints.ai_family = family;
     470             : 
     471             :   /**
     472             :    * ARES_AI_NOSORT result addresses will not be sorted and no connections to resolved addresses
     473             :    * will be attempted
     474             :    */
     475           0 :   hints.ai_flags = ARES_AI_NOSORT;
     476             : 
     477           0 :   ares_getaddrinfo(
     478           0 :       channel_, dns_name_.c_str(), /* service */ nullptr, &hints,
     479           0 :       [](void* arg, int status, int timeouts, ares_addrinfo* addrinfo) {
     480           0 :         static_cast<AddrInfoPendingResolution*>(arg)->onAresGetAddrInfoCallback(status, timeouts,
     481           0 :                                                                                 addrinfo);
     482           0 :       },
     483           0 :       this);
     484           0 : }
     485             : 
     486             : DnsResolverImpl::AddrInfoPendingResolution::AvailableInterfaces
     487           0 : DnsResolverImpl::AddrInfoPendingResolution::availableInterfaces() {
     488           0 :   if (!Api::OsSysCallsSingleton::get().supportsGetifaddrs()) {
     489             :     // Maintain no-op behavior if the system cannot provide interface information.
     490           0 :     return {true, true};
     491           0 :   }
     492             : 
     493           0 :   Api::InterfaceAddressVector interface_addresses{};
     494           0 :   const Api::SysCallIntResult rc = Api::OsSysCallsSingleton::get().getifaddrs(interface_addresses);
     495           0 :   if (rc.return_value_ != 0) {
     496           0 :     ENVOY_LOG_EVENT(debug, "cares_getifaddrs_error",
     497           0 :                     "dns resolution for {} could not obtain interface information with error={}",
     498           0 :                     dns_name_, rc.errno_);
     499             :     // Maintain no-op behavior if the system encountered an error while providing interface
     500             :     // information.
     501           0 :     return {true, true};
     502           0 :   }
     503             : 
     504           0 :   DnsResolverImpl::AddrInfoPendingResolution::AvailableInterfaces available_interfaces{false,
     505           0 :                                                                                        false};
     506           0 :   for (const auto& interface_address : interface_addresses) {
     507           0 :     if (!interface_address.interface_addr_->ip()) {
     508           0 :       continue;
     509           0 :     }
     510             : 
     511           0 :     if (Network::Utility::isLoopbackAddress(*interface_address.interface_addr_)) {
     512           0 :       continue;
     513           0 :     }
     514             : 
     515           0 :     switch (interface_address.interface_addr_->ip()->version()) {
     516           0 :     case Network::Address::IpVersion::v4:
     517           0 :       available_interfaces.v4_available_ = true;
     518           0 :       if (available_interfaces.v6_available_) {
     519           0 :         return available_interfaces;
     520           0 :       }
     521           0 :       break;
     522           0 :     case Network::Address::IpVersion::v6:
     523           0 :       available_interfaces.v6_available_ = true;
     524           0 :       if (available_interfaces.v4_available_) {
     525           0 :         return available_interfaces;
     526           0 :       }
     527           0 :       break;
     528           0 :     }
     529           0 :   }
     530           0 :   return available_interfaces;
     531           0 : }
     532             : 
     533             : // c-ares DNS resolver factory
     534             : class CaresDnsResolverFactory : public DnsResolverFactory,
     535             :                                 public Logger::Loggable<Logger::Id::dns> {
     536             : public:
     537         147 :   std::string name() const override { return std::string(CaresDnsResolver); }
     538             : 
     539          12 :   ProtobufTypes::MessagePtr createEmptyConfigProto() override {
     540          12 :     return ProtobufTypes::MessagePtr{
     541          12 :         new envoy::extensions::network::dns_resolver::cares::v3::CaresDnsResolverConfig()};
     542          12 :   }
     543             : 
     544             :   DnsResolverSharedPtr createDnsResolver(Event::Dispatcher& dispatcher, Api::Api& api,
     545             :                                          const envoy::config::core::v3::TypedExtensionConfig&
     546           0 :                                              typed_dns_resolver_config) const override {
     547           0 :     envoy::extensions::network::dns_resolver::cares::v3::CaresDnsResolverConfig cares;
     548           0 :     std::vector<Network::Address::InstanceConstSharedPtr> resolvers;
     549             : 
     550           0 :     ASSERT(dispatcher.isThreadSafe());
     551             :     // Only c-ares DNS factory will call into this function.
     552             :     // Directly unpack the typed config to a c-ares object.
     553           0 :     Envoy::MessageUtil::unpackTo(typed_dns_resolver_config.typed_config(), cares);
     554           0 :     if (!cares.resolvers().empty()) {
     555           0 :       const auto& resolver_addrs = cares.resolvers();
     556           0 :       resolvers.reserve(resolver_addrs.size());
     557           0 :       for (const auto& resolver_addr : resolver_addrs) {
     558           0 :         resolvers.push_back(Network::Address::resolveProtoAddress(resolver_addr));
     559           0 :       }
     560           0 :     }
     561           0 :     return std::make_shared<Network::DnsResolverImpl>(cares, dispatcher, resolvers,
     562           0 :                                                       api.rootScope());
     563           0 :   }
     564             : 
     565           0 :   void initialize() override {
     566             :     // Initialize c-ares library in case first time.
     567           0 :     absl::MutexLock lock(&mutex_);
     568           0 :     if (!ares_library_initialized_) {
     569           0 :       ares_library_initialized_ = true;
     570           0 :       ENVOY_LOG(debug, "c-ares library initialized.");
     571           0 :       ares_library_init(ARES_LIB_INIT_ALL);
     572           0 :     }
     573           0 :   }
     574          13 :   void terminate() override {
     575             :     // Cleanup c-ares library if initialized.
     576          13 :     absl::MutexLock lock(&mutex_);
     577          13 :     if (ares_library_initialized_) {
     578           0 :       ares_library_initialized_ = false;
     579           0 :       ENVOY_LOG(debug, "c-ares library cleaned up.");
     580           0 :       ares_library_cleanup();
     581           0 :     }
     582          13 :   }
     583             : 
     584             : private:
     585             :   bool ares_library_initialized_ ABSL_GUARDED_BY(mutex_){false};
     586             :   absl::Mutex mutex_;
     587             : };
     588             : 
     589             : // Register the CaresDnsResolverFactory
     590             : REGISTER_FACTORY(CaresDnsResolverFactory, DnsResolverFactory);
     591             : 
     592             : } // namespace Network
     593             : } // namespace Envoy

Generated by: LCOV version 1.15