Line data Source code
1 : #pragma once 2 : 3 : #include <cstdint> 4 : #include <string> 5 : 6 : #include "envoy/common/platform.h" 7 : #include "envoy/event/dispatcher.h" 8 : #include "envoy/event/file_event.h" 9 : #include "envoy/network/dns.h" 10 : #include "envoy/registry/registry.h" 11 : 12 : #include "source/common/common/linked_object.h" 13 : #include "source/common/common/logger.h" 14 : #include "source/common/common/utility.h" 15 : #include "source/common/network/dns_resolver/dns_factory_util.h" 16 : 17 : #include "absl/container/node_hash_map.h" 18 : #include "ares.h" 19 : 20 : namespace Envoy { 21 : namespace Network { 22 : 23 : /** 24 : * All DNS stats. @see stats_macros.h 25 : */ 26 : #define ALL_CARES_DNS_RESOLVER_STATS(COUNTER, GAUGE) \ 27 0 : COUNTER(resolve_total) \ 28 0 : GAUGE(pending_resolutions, NeverImport) \ 29 0 : COUNTER(not_found) \ 30 0 : COUNTER(get_addr_failure) \ 31 0 : COUNTER(timeouts) 32 : 33 : /** 34 : * Struct definition for all DNS stats. @see stats_macros.h 35 : */ 36 : struct CaresDnsResolverStats { 37 : ALL_CARES_DNS_RESOLVER_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT) 38 : }; 39 : 40 : class DnsResolverImplPeer; 41 : 42 : /** 43 : * Implementation of DnsResolver that uses c-ares. All calls and callbacks are assumed to 44 : * happen on the thread that owns the creating dispatcher. 45 : */ 46 : class DnsResolverImpl : public DnsResolver, protected Logger::Loggable<Logger::Id::dns> { 47 : public: 48 : DnsResolverImpl( 49 : const envoy::extensions::network::dns_resolver::cares::v3::CaresDnsResolverConfig& config, 50 : Event::Dispatcher& dispatcher, 51 : const std::vector<Network::Address::InstanceConstSharedPtr>& resolvers, 52 : Stats::Scope& root_scope); 53 : ~DnsResolverImpl() override; 54 : 55 : static CaresDnsResolverStats generateCaresDnsResolverStats(Stats::Scope& scope); 56 : 57 : // Network::DnsResolver 58 : ActiveDnsQuery* resolve(const std::string& dns_name, DnsLookupFamily dns_lookup_family, 59 : ResolveCb callback) override; 60 0 : void resetNetworking() override { 61 : // Dirty the channel so that the next query will recreate it. 62 0 : dirty_channel_ = true; 63 0 : } 64 : 65 : private: 66 : friend class DnsResolverImplPeer; 67 : class PendingResolution : public ActiveDnsQuery { 68 : public: 69 0 : void cancel(CancelReason reason) override { 70 : // c-ares only supports channel-wide cancellation, so we just allow the 71 : // network events to continue but don't invoke the callback on completion. 72 : // TODO(mattklein123): Potentially use timeout to destroy and recreate the channel. 73 0 : cancelled_ = true; 74 0 : cancel_reason_ = reason; 75 0 : } 76 : // Does the object own itself? Resource reclamation occurs via self-deleting 77 : // on query completion or error. 78 : bool owned_ = false; 79 : // Has the query completed? Only meaningful if !owned_; 80 : bool completed_ = false; 81 : 82 : protected: 83 : // Network::ActiveDnsQuery 84 : PendingResolution(DnsResolverImpl& parent, ResolveCb callback, Event::Dispatcher& dispatcher, 85 : ares_channel channel, const std::string& dns_name) 86 : : parent_(parent), callback_(callback), dispatcher_(dispatcher), channel_(channel), 87 0 : dns_name_(dns_name) {} 88 : 89 : void finishResolve(); 90 : 91 : DnsResolverImpl& parent_; 92 : // Caller supplied callback to invoke on query completion or error. 93 : const ResolveCb callback_; 94 : // Dispatcher to post any callback_ exceptions to. 95 : Event::Dispatcher& dispatcher_; 96 : // Was the query cancelled via cancel()? 97 : bool cancelled_ = false; 98 : const ares_channel channel_; 99 : const std::string dns_name_; 100 : CancelReason cancel_reason_; 101 : 102 : // Small wrapping struct to accumulate addresses from firings of the 103 : // onAresGetAddrInfoCallback callback. 104 : struct PendingResponse { 105 : ResolutionStatus status_; 106 : std::list<DnsResponse> address_list_; 107 : }; 108 : 109 : // Note: pending_response_ is constructed with ResolutionStatus::Failure by default and 110 : // __only__ changed to ResolutionStatus::Success if there is an `ARES_SUCCESS` or `ARES_ENODATA` 111 : // or `ARES_ENOTFOUND`reply. 112 : // In the dual_resolution case __any__ ARES_SUCCESS reply will result in a 113 : // ResolutionStatus::Success callback. 114 : PendingResponse pending_response_{ResolutionStatus::Failure, {}}; 115 : }; 116 : 117 : class AddrInfoPendingResolution final : public PendingResolution { 118 : public: 119 : AddrInfoPendingResolution(DnsResolverImpl& parent, ResolveCb callback, 120 : Event::Dispatcher& dispatcher, ares_channel channel, 121 : const std::string& dns_name, DnsLookupFamily dns_lookup_family); 122 : ~AddrInfoPendingResolution() override; 123 : 124 : /** 125 : * ares_getaddrinfo query callback. 126 : * @param status return status of call to ares_getaddrinfo. 127 : * @param timeouts the number of times the request timed out. 128 : * @param addrinfo structure to store address info. 129 : */ 130 : void onAresGetAddrInfoCallback(int status, int timeouts, ares_addrinfo* addrinfo); 131 : 132 : /** 133 : * wrapper function of call to ares_getaddrinfo. 134 : */ 135 : void startResolution(); 136 : 137 : private: 138 : void startResolutionImpl(int family); 139 : bool isResponseWithNoRecords(int status); 140 : 141 : // Holds the availability of non-loopback network interfaces for the system. 142 : struct AvailableInterfaces { 143 : bool v4_available_; 144 : bool v6_available_; 145 : }; 146 : 147 : // Return the currently available network interfaces. 148 : // Note: this call uses syscalls. 149 : AvailableInterfaces availableInterfaces(); 150 : 151 : // Perform a second resolution under certain conditions. If dns_lookup_family_ is V4Preferred 152 : // or Auto: perform a second resolution if the first one fails. If dns_lookup_family_ is All: 153 : // perform resolutions on both families concurrently. 154 : bool dual_resolution_ = false; 155 : // The number of outstanding pending resolutions. This should always be 0 or 1 for all lookup 156 : // types other than All. For all, this can be be 0-2 as both queries are issued in parallel. 157 : // This is used mostly for assertions but is used in the ARES_EDESTRUCTION path to make sure 158 : // all concurrent queries are unwound before cleaning up the resolution. 159 : uint32_t pending_resolutions_ = 0; 160 : int family_ = AF_INET; 161 : const DnsLookupFamily dns_lookup_family_; 162 : // Queried for at construction time. 163 : const AvailableInterfaces available_interfaces_; 164 : }; 165 : 166 : struct AresOptions { 167 : ares_options options_; 168 : int optmask_; 169 : }; 170 : 171 : static absl::optional<std::string> 172 : maybeBuildResolversCsv(const std::vector<Network::Address::InstanceConstSharedPtr>& resolvers); 173 : 174 : // Callback for events on sockets tracked in events_. 175 : void onEventCallback(os_fd_t fd, uint32_t events); 176 : // c-ares callback when a socket state changes, indicating that libevent 177 : // should listen for read/write events. 178 : void onAresSocketStateChange(os_fd_t fd, int read, int write); 179 : // Initialize the channel. 180 : void initializeChannel(ares_options* options, int optmask); 181 : // Check if the only nameserver available is the c-ares default. 182 : bool isCaresDefaultTheOnlyNameserver(); 183 : // Update timer for c-ares timeouts. 184 : void updateAresTimer(); 185 : // Return default AresOptions. 186 : AresOptions defaultAresOptions(); 187 : 188 : void chargeGetAddrInfoErrorStats(int status, int timeouts); 189 : 190 : Event::Dispatcher& dispatcher_; 191 : Event::TimerPtr timer_; 192 : ares_channel channel_; 193 : bool dirty_channel_{}; 194 : envoy::config::core::v3::DnsResolverOptions dns_resolver_options_; 195 : 196 : absl::node_hash_map<int, Event::FileEventPtr> events_; 197 : const bool use_resolvers_as_fallback_; 198 : const absl::optional<std::string> resolvers_csv_; 199 : const bool filter_unroutable_families_; 200 : Stats::ScopeSharedPtr scope_; 201 : CaresDnsResolverStats stats_; 202 : }; 203 : 204 : DECLARE_FACTORY(CaresDnsResolverFactory); 205 : 206 : } // namespace Network 207 : } // namespace Envoy