LCOV - code coverage report
Current view: top level - source/extensions/network/dns_resolver/getaddrinfo - getaddrinfo.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 5 139 3.6 %
Date: 2024-01-05 06:35:25 Functions: 2 17 11.8 %

          Line data    Source code
       1             : #include "source/extensions/network/dns_resolver/getaddrinfo/getaddrinfo.h"
       2             : 
       3             : #include "envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.pb.h"
       4             : #include "envoy/network/dns_resolver.h"
       5             : 
       6             : #include "source/common/api/os_sys_calls_impl.h"
       7             : #include "source/common/network/address_impl.h"
       8             : 
       9             : namespace Envoy {
      10             : namespace Network {
      11             : 
      12             : // This resolver uses getaddrinfo() on a dedicated resolution thread. Thus, it is only suitable
      13             : // currently for relatively low rate resolutions. In the future a thread pool could be added if
      14             : // desired.
      15             : class GetAddrInfoDnsResolver : public DnsResolver, public Logger::Loggable<Logger::Id::dns> {
      16             : public:
      17             :   GetAddrInfoDnsResolver(Event::Dispatcher& dispatcher, Api::Api& api)
      18             :       : dispatcher_(dispatcher),
      19           0 :         resolver_thread_(api.threadFactory().createThread([this] { resolveThreadRoutine(); })) {}
      20             : 
      21           0 :   ~GetAddrInfoDnsResolver() override {
      22           0 :     {
      23           0 :       absl::MutexLock guard(&mutex_);
      24           0 :       shutting_down_ = true;
      25           0 :     }
      26             : 
      27           0 :     resolver_thread_->join();
      28           0 :   }
      29             : 
      30             :   // DnsResolver
      31             :   ActiveDnsQuery* resolve(const std::string& dns_name, DnsLookupFamily dns_lookup_family,
      32           0 :                           ResolveCb callback) override {
      33           0 :     ENVOY_LOG(debug, "adding new query [{}] to pending queries", dns_name);
      34           0 :     auto new_query = std::make_unique<PendingQuery>(dns_name, dns_lookup_family, callback);
      35           0 :     absl::MutexLock guard(&mutex_);
      36           0 :     pending_queries_.emplace_back(std::move(new_query));
      37           0 :     return pending_queries_.back().get();
      38           0 :   }
      39             : 
      40           0 :   void resetNetworking() override {}
      41             : 
      42             : private:
      43             :   class PendingQuery : public ActiveDnsQuery {
      44             :   public:
      45             :     PendingQuery(const std::string& dns_name, DnsLookupFamily dns_lookup_family, ResolveCb callback)
      46           0 :         : dns_name_(dns_name), dns_lookup_family_(dns_lookup_family), callback_(callback) {}
      47             : 
      48           0 :     void cancel(CancelReason) override {
      49           0 :       ENVOY_LOG(debug, "cancelling query [{}]", dns_name_);
      50           0 :       cancelled_ = true;
      51           0 :     }
      52             : 
      53             :     const std::string dns_name_;
      54             :     const DnsLookupFamily dns_lookup_family_;
      55             :     ResolveCb callback_;
      56             :     bool cancelled_{false};
      57             :   };
      58             :   // Must be a shared_ptr for passing around via post.
      59             :   using PendingQuerySharedPtr = std::shared_ptr<PendingQuery>;
      60             : 
      61             :   // RAII wrapper to free the `addrinfo`.
      62             :   class AddrInfoWrapper : NonCopyable {
      63             :   public:
      64           0 :     AddrInfoWrapper(addrinfo* info) : info_(info) {}
      65           0 :     ~AddrInfoWrapper() {
      66           0 :       if (info_ != nullptr) {
      67           0 :         Api::OsSysCallsSingleton::get().freeaddrinfo(info_);
      68           0 :       }
      69           0 :     }
      70           0 :     const addrinfo* get() { return info_; }
      71             : 
      72             :   private:
      73             :     addrinfo* info_;
      74             :   };
      75             : 
      76             :   // Parse a getaddrinfo() response and determine the final address list. We could potentially avoid
      77             :   // adding v4 or v6 addresses if we know they will never be used. Right now the final filtering is
      78             :   // done below and this code is kept simple.
      79             :   std::pair<ResolutionStatus, std::list<DnsResponse>>
      80           0 :   processResponse(const PendingQuery& query, const addrinfo* addrinfo_result) {
      81           0 :     std::list<DnsResponse> v4_results;
      82           0 :     std::list<DnsResponse> v6_results;
      83           0 :     for (auto ai = addrinfo_result; ai != nullptr; ai = ai->ai_next) {
      84           0 :       if (ai->ai_family == AF_INET) {
      85           0 :         sockaddr_in address;
      86           0 :         memset(&address, 0, sizeof(address));
      87           0 :         address.sin_family = AF_INET;
      88           0 :         address.sin_port = 0;
      89           0 :         address.sin_addr = reinterpret_cast<sockaddr_in*>(ai->ai_addr)->sin_addr;
      90             : 
      91           0 :         v4_results.emplace_back(
      92           0 :             DnsResponse(std::make_shared<const Address::Ipv4Instance>(&address), DEFAULT_TTL));
      93           0 :       } else if (ai->ai_family == AF_INET6) {
      94           0 :         sockaddr_in6 address;
      95           0 :         memset(&address, 0, sizeof(address));
      96           0 :         address.sin6_family = AF_INET6;
      97           0 :         address.sin6_port = 0;
      98           0 :         address.sin6_addr = reinterpret_cast<sockaddr_in6*>(ai->ai_addr)->sin6_addr;
      99           0 :         v6_results.emplace_back(
     100           0 :             DnsResponse(std::make_shared<const Address::Ipv6Instance>(address), DEFAULT_TTL));
     101           0 :       }
     102           0 :     }
     103             : 
     104           0 :     std::list<DnsResponse> final_results;
     105           0 :     switch (query.dns_lookup_family_)
     106           0 :     case DnsLookupFamily::All: {
     107           0 :       final_results = std::move(v4_results);
     108           0 :       final_results.splice(final_results.begin(), v6_results);
     109           0 :       break;
     110           0 :     case DnsLookupFamily::V4Only:
     111           0 :       final_results = std::move(v4_results);
     112           0 :       break;
     113           0 :     case DnsLookupFamily::V6Only:
     114           0 :       final_results = std::move(v6_results);
     115           0 :       break;
     116           0 :     case DnsLookupFamily::V4Preferred:
     117           0 :       if (!v4_results.empty()) {
     118           0 :         final_results = std::move(v4_results);
     119           0 :       } else {
     120           0 :         final_results = std::move(v6_results);
     121           0 :       }
     122           0 :       break;
     123           0 :     case DnsLookupFamily::Auto:
     124             :       // This is effectively V6Preferred.
     125           0 :       if (!v6_results.empty()) {
     126           0 :         final_results = std::move(v6_results);
     127           0 :       } else {
     128           0 :         final_results = std::move(v4_results);
     129           0 :       }
     130           0 :       break;
     131           0 :     }
     132             : 
     133           0 :       ENVOY_LOG(
     134           0 :           debug, "getaddrinfo resolution complete for host '{}': {}", query.dns_name_,
     135           0 :           accumulateToString<Network::DnsResponse>(final_results, [](const auto& dns_response) {
     136           0 :             return dns_response.addrInfo().address_->asString();
     137           0 :           }));
     138             : 
     139           0 :     return std::make_pair(ResolutionStatus::Success, final_results);
     140           0 :   }
     141             : 
     142             :   // Background thread which wakes up and does resolutions.
     143           0 :   void resolveThreadRoutine() {
     144           0 :     ENVOY_LOG(debug, "starting getaddrinfo resolver thread");
     145             : 
     146           0 :     while (true) {
     147           0 :       PendingQuerySharedPtr next_query;
     148           0 :       {
     149           0 :         absl::MutexLock guard(&mutex_);
     150           0 :         auto condition = [this]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
     151           0 :           return shutting_down_ || !pending_queries_.empty();
     152           0 :         };
     153           0 :         mutex_.Await(absl::Condition(&condition));
     154           0 :         if (shutting_down_) {
     155           0 :           break;
     156           0 :         }
     157             : 
     158           0 :         next_query = std::move(pending_queries_.front());
     159           0 :         pending_queries_.pop_front();
     160           0 :       }
     161             : 
     162           0 :       ENVOY_LOG(debug, "popped pending query [{}]", next_query->dns_name_);
     163             : 
     164             :       // For mock testing make sure the getaddrinfo() response is freed prior to the post.
     165           0 :       std::pair<ResolutionStatus, std::list<DnsResponse>> response;
     166           0 :       {
     167           0 :         addrinfo hints;
     168           0 :         memset(&hints, 0, sizeof(hints));
     169           0 :         hints.ai_flags = AI_ADDRCONFIG;
     170           0 :         hints.ai_family = AF_UNSPEC;
     171             :         // If we don't specify a socket type, every address will appear twice, once
     172             :         // for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family
     173             :         // anyway, just pick one.
     174           0 :         hints.ai_socktype = SOCK_STREAM;
     175           0 :         addrinfo* addrinfo_result_do_not_use = nullptr;
     176           0 :         auto rc = Api::OsSysCallsSingleton::get().getaddrinfo(
     177           0 :             next_query->dns_name_.c_str(), nullptr, &hints, &addrinfo_result_do_not_use);
     178           0 :         auto addrinfo_wrapper = AddrInfoWrapper(addrinfo_result_do_not_use);
     179           0 :         if (rc.return_value_ == 0) {
     180           0 :           response = processResponse(*next_query, addrinfo_wrapper.get());
     181           0 :         } else {
     182             :           // TODO(mattklein123): Handle some errors differently such as `EAI_NODATA`.
     183           0 :           ENVOY_LOG(debug, "getaddrinfo failed with rc={} errno={}", gai_strerror(rc.return_value_),
     184           0 :                     errorDetails(rc.errno_));
     185           0 :           response = std::make_pair(ResolutionStatus::Failure, std::list<DnsResponse>());
     186           0 :         }
     187           0 :       }
     188             : 
     189           0 :       dispatcher_.post(
     190           0 :           [finished_query = std::move(next_query), response = std::move(response)]() mutable {
     191           0 :             if (finished_query->cancelled_) {
     192           0 :               ENVOY_LOG(debug, "dropping cancelled query [{}]", finished_query->dns_name_);
     193           0 :             } else {
     194           0 :               finished_query->callback_(response.first, std::move(response.second));
     195           0 :             }
     196           0 :           });
     197           0 :     }
     198             : 
     199           0 :     ENVOY_LOG(debug, "getaddrinfo resolver thread exiting");
     200           0 :   }
     201             : 
     202             :   // getaddrinfo() doesn't provide TTL so use a hard coded default. This can be made configurable
     203             :   // later if needed.
     204             :   static constexpr std::chrono::seconds DEFAULT_TTL = std::chrono::seconds(60);
     205             : 
     206             :   Event::Dispatcher& dispatcher_;
     207             :   absl::Mutex mutex_;
     208             :   std::list<PendingQuerySharedPtr> pending_queries_ ABSL_GUARDED_BY(mutex_);
     209             :   bool shutting_down_ ABSL_GUARDED_BY(mutex_){};
     210             :   // The resolver thread must be initialized last so that the above members are already fully
     211             :   // initialized.
     212             :   const Thread::ThreadPtr resolver_thread_;
     213             : };
     214             : 
     215             : // getaddrinfo DNS resolver factory
     216             : class GetAddrInfoDnsResolverFactory : public DnsResolverFactory,
     217             :                                       public Logger::Loggable<Logger::Id::dns> {
     218             : public:
     219          38 :   std::string name() const override { return {"envoy.network.dns_resolver.getaddrinfo"}; }
     220             : 
     221           1 :   ProtobufTypes::MessagePtr createEmptyConfigProto() override {
     222           1 :     return ProtobufTypes::MessagePtr{new envoy::extensions::network::dns_resolver::getaddrinfo::v3::
     223           1 :                                          GetAddrInfoDnsResolverConfig()};
     224           1 :   }
     225             : 
     226             :   DnsResolverSharedPtr
     227             :   createDnsResolver(Event::Dispatcher& dispatcher, Api::Api& api,
     228           0 :                     const envoy::config::core::v3::TypedExtensionConfig&) const override {
     229           0 :     return std::make_shared<GetAddrInfoDnsResolver>(dispatcher, api);
     230           0 :   }
     231             : };
     232             : 
     233             : // Register the CaresDnsResolverFactory
     234             : REGISTER_FACTORY(GetAddrInfoDnsResolverFactory, DnsResolverFactory);
     235             : 
     236             : } // namespace Network
     237             : } // namespace Envoy

Generated by: LCOV version 1.15