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
|