1
#include "source/extensions/network/dns_resolver/cares/dns_impl.h"
2

            
3
#include <ares.h>
4

            
5
#include <chrono>
6
#include <cstdint>
7
#include <limits>
8
#include <list>
9
#include <memory>
10
#include <string>
11

            
12
#include "envoy/common/platform.h"
13
#include "envoy/registry/registry.h"
14

            
15
#include "source/common/api/os_sys_calls_impl.h"
16
#include "source/common/common/assert.h"
17
#include "source/common/common/fmt.h"
18
#include "source/common/common/thread.h"
19
#include "source/common/network/address_impl.h"
20
#include "source/common/network/resolver_impl.h"
21
#include "source/common/network/utility.h"
22
#include "source/common/protobuf/protobuf.h"
23
#include "source/common/protobuf/utility.h"
24
#include "source/common/runtime/runtime_features.h"
25

            
26
#include "absl/strings/str_join.h"
27
#include "ares.h"
28

            
29
namespace Envoy {
30
namespace Network {
31
namespace {
32
// In late 2023, c-ares modified its default DNS timeout and retry behavior during a major refactor.
33
// See: https://github.com/c-ares/c-ares/pull/542/files
34
//
35
// After user reports of increased DNS resolution times in v1.31.0, these defaults were restored
36
// to their original values: 5 second timeout and 4 retry attempts.
37
// Ref: https://github.com/envoyproxy/envoy/issues/35117
38
constexpr uint32_t DEFAULT_QUERY_TIMEOUT_SECONDS = 5;
39
constexpr uint32_t DEFAULT_QUERY_TRIES = 4;
40
} // namespace
41

            
42
DnsResolverImpl::DnsResolverImpl(
43
    const envoy::extensions::network::dns_resolver::cares::v3::CaresDnsResolverConfig& config,
44
    Event::Dispatcher& dispatcher, absl::optional<std::string> resolvers_csv,
45
    Stats::Scope& root_scope)
46
178
    : dispatcher_(dispatcher),
47
178
      timer_(dispatcher.createTimer([this] { onEventCallback(ARES_SOCKET_BAD, 0); })),
48
178
      dns_resolver_options_(config.dns_resolver_options()),
49
178
      use_resolvers_as_fallback_(config.use_resolvers_as_fallback()),
50
      udp_max_queries_(
51
178
          static_cast<uint32_t>(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, udp_max_queries, 0))),
52
178
      query_timeout_seconds_(static_cast<uint64_t>(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
53
178
          config, query_timeout_seconds, DEFAULT_QUERY_TIMEOUT_SECONDS))),
54
178
      query_tries_(static_cast<uint32_t>(
55
178
          PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, query_tries, DEFAULT_QUERY_TRIES))),
56
178
      rotate_nameservers_(config.rotate_nameservers()),
57
178
      edns0_max_payload_size_(static_cast<uint32_t>(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
58
178
          config, edns0_max_payload_size, 0))), // 0 means use c-ares default EDNS0
59
      max_udp_channel_duration_(
60
178
          config.has_max_udp_channel_duration()
61
178
              ? std::chrono::milliseconds(Protobuf::util::TimeUtil::DurationToMilliseconds(
62
3
                    config.max_udp_channel_duration()))
63
178
              : std::chrono::milliseconds::zero()),
64
178
      reinit_channel_on_timeout_(config.reinit_channel_on_timeout()), resolvers_csv_(resolvers_csv),
65
178
      filter_unroutable_families_(config.filter_unroutable_families()),
66
178
      scope_(root_scope.createScope("dns.cares.")), stats_(generateCaresDnsResolverStats(*scope_)) {
67
178
  AresOptions options = defaultAresOptions();
68
178
  initializeChannel(&options.options_, options.optmask_);
69

            
70
  // Initialize the periodic UDP channel refresh timer if configured.
71
178
  if (max_udp_channel_duration_ > std::chrono::milliseconds::zero()) {
72
7
    udp_channel_refresh_timer_ = dispatcher.createTimer([this] { onUdpChannelRefreshTimer(); });
73
3
    udp_channel_refresh_timer_->enableTimer(max_udp_channel_duration_);
74
3
  }
75
178
}
76

            
77
178
DnsResolverImpl::~DnsResolverImpl() {
78
178
  timer_->disableTimer();
79
178
  if (udp_channel_refresh_timer_) {
80
3
    udp_channel_refresh_timer_->disableTimer();
81
3
  }
82
178
  ares_destroy(channel_);
83
178
}
84

            
85
178
CaresDnsResolverStats DnsResolverImpl::generateCaresDnsResolverStats(Stats::Scope& scope) {
86
178
  return {ALL_CARES_DNS_RESOLVER_STATS(POOL_COUNTER(scope), POOL_GAUGE(scope))};
87
178
}
88

            
89
absl::StatusOr<absl::optional<std::string>> DnsResolverImpl::maybeBuildResolversCsv(
90
178
    const std::vector<Network::Address::InstanceConstSharedPtr>& resolvers) {
91
178
  if (resolvers.empty()) {
92
166
    return absl::nullopt;
93
166
  }
94

            
95
12
  std::vector<std::string> resolver_addrs;
96
12
  resolver_addrs.reserve(resolvers.size());
97
18
  for (const auto& resolver : resolvers) {
98
    // This should be an IP address (i.e. not a pipe).
99
18
    if (resolver->ip() == nullptr) {
100
1
      return absl::InvalidArgumentError(
101
1
          fmt::format("DNS resolver '{}' is not an IP address", resolver->asString()));
102
1
    }
103
    // Note that the ip()->port() may be zero if the port is not fully specified by the
104
    // Address::Instance.
105
    // resolver->asString() is avoided as that format may be modified by custom
106
    // Address::Instance implementations in ways that make the <port> not a simple
107
    // integer. See https://github.com/envoyproxy/envoy/pull/3366.
108
17
    if (resolver->ip()->ipv6()) {
109
5
      resolver_addrs.push_back(
110
5
          fmt::format("[{}]:{}", resolver->ip()->addressAsString(), resolver->ip()->port()));
111
12
    } else {
112
12
      resolver_addrs.push_back(
113
12
          fmt::format("{}:{}", resolver->ip()->addressAsString(), resolver->ip()->port()));
114
12
    }
115
17
  }
116
11
  return {absl::StrJoin(resolver_addrs, ",")};
117
12
}
118

            
119
190
DnsResolverImpl::AresOptions DnsResolverImpl::defaultAresOptions() {
120
190
  AresOptions options{};
121

            
122
190
  if (dns_resolver_options_.use_tcp_for_dns_lookups()) {
123
3
    options.optmask_ |= ARES_OPT_FLAGS;
124
3
    options.options_.flags |= ARES_FLAG_USEVC;
125
3
  }
126

            
127
190
  if (dns_resolver_options_.no_default_search_domain()) {
128
1
    options.optmask_ |= ARES_OPT_FLAGS;
129
1
    options.options_.flags |= ARES_FLAG_NOSEARCH;
130
1
  }
131

            
132
190
  if (udp_max_queries_ > 0) {
133
1
    options.optmask_ |= ARES_OPT_UDP_MAX_QUERIES;
134
1
    options.options_.udp_max_queries = udp_max_queries_;
135
1
  }
136

            
137
190
  options.optmask_ |= ARES_OPT_TIMEOUT;
138
190
  options.options_.timeout = query_timeout_seconds_;
139
190
  options.optmask_ |= ARES_OPT_TRIES;
140
190
  options.options_.tries = query_tries_;
141

            
142
190
  if (rotate_nameservers_) {
143
1
    options.optmask_ |= ARES_OPT_ROTATE;
144
189
  } else {
145
189
    options.optmask_ |= ARES_OPT_NOROTATE;
146
189
  }
147

            
148
  // Configure EDNS0 payload size if specified
149
190
  if (edns0_max_payload_size_ > 0) {
150
1
    options.optmask_ |= ARES_OPT_EDNSPSZ;
151
1
    options.options_.ednspsz = edns0_max_payload_size_;
152
1
  }
153

            
154
  // Disable query cache by default.
155
190
  options.optmask_ |= ARES_OPT_QUERY_CACHE;
156
190
  options.options_.qcache_max_ttl = 0;
157

            
158
190
  return options;
159
190
}
160

            
161
2
bool DnsResolverImpl::isCaresDefaultTheOnlyNameserver() {
162
2
  struct ares_addr_port_node* servers{};
163
2
  int result = ares_get_servers_ports(channel_, &servers);
164
2
  RELEASE_ASSERT(result == ARES_SUCCESS, "failure in ares_get_servers_ports");
165
  // as determined in init_by_defaults in ares_init.c.
166
2
  const bool has_only_default_nameserver =
167
2
      servers == nullptr || (servers->next == nullptr && servers->family == AF_INET &&
168
2
                             servers->addr.addr4.s_addr == htonl(INADDR_LOOPBACK) &&
169
2
                             servers->udp_port == 0 && servers->tcp_port == 0);
170
2
  if (servers != nullptr) {
171
2
    ares_free_data(servers);
172
2
  }
173
2
  return has_only_default_nameserver;
174
2
}
175

            
176
238
void DnsResolverImpl::initializeChannel(ares_options* options, int optmask) {
177
497
  options->sock_state_cb = [](void* arg, os_fd_t fd, int read, int write) {
178
486
    static_cast<DnsResolverImpl*>(arg)->onAresSocketStateChange(fd, read, write);
179
486
  };
180
238
  options->sock_state_cb_data = this;
181
238
  int result = ares_init_options(&channel_, options, optmask | ARES_OPT_SOCK_STATE_CB);
182
238
  RELEASE_ASSERT(result == ARES_SUCCESS, "ares_init_options failed");
183

            
184
238
  if (resolvers_csv_.has_value()) {
185
12
    bool use_resolvers = true;
186
    // If the only name server available is c-ares' default then fallback to the user defined
187
    // resolvers. Otherwise, use the resolvers provided by c-ares.
188
12
    if (use_resolvers_as_fallback_ && !isCaresDefaultTheOnlyNameserver()) {
189
1
      use_resolvers = false;
190
1
    }
191

            
192
12
    if (use_resolvers) {
193
11
      int result = ares_set_servers_ports_csv(channel_, resolvers_csv_->c_str());
194
11
      RELEASE_ASSERT(result == ARES_SUCCESS, "");
195
11
    }
196
12
  }
197
238
}
198

            
199
// Treat responses with `ARES_ENODATA` or `ARES_ENOTFOUND` status as DNS response with no records.
200
// @see DnsResolverImpl::PendingResolution::onAresGetAddrInfoCallback for details.
201
192
bool DnsResolverImpl::AddrInfoPendingResolution::isResponseWithNoRecords(int status) {
202
192
  return status == ARES_ENODATA || status == ARES_ENOTFOUND;
203
192
}
204

            
205
void DnsResolverImpl::AddrInfoPendingResolution::onAresGetAddrInfoCallback(
206
232
    int status, int timeouts, ares_addrinfo* addrinfo) {
207
232
  ASSERT(pending_resolutions_ > 0);
208
232
  pending_resolutions_--;
209

            
210
232
  parent_.stats_.resolve_total_.inc();
211
232
  parent_.stats_.pending_resolutions_.dec();
212

            
213
232
  if (status != ARES_SUCCESS) {
214
131
    parent_.chargeGetAddrInfoErrorStats(status, timeouts);
215

            
216
131
    if (!isResponseWithNoRecords(status)) {
217
94
      ENVOY_LOG_EVENT(debug, "cares_resolution_failure",
218
94
                      "dns resolution for {} failed with c-ares status {:#06x}: \"{}\"", dns_name_,
219
94
                      status, ares_strerror(status));
220
97
    } else {
221
37
      ENVOY_LOG_EVENT(debug, "cares_resolution_no_records",
222
37
                      "dns resolution without records for {} c-ares status {:#06x}: \"{}\"",
223
37
                      dns_name_, status, ares_strerror(status));
224
37
    }
225
131
  }
226

            
227
  // We receive ARES_EDESTRUCTION when destructing with pending queries.
228
232
  if (status == ARES_EDESTRUCTION) {
229
    // In the destruction path we must wait until there are no more pending queries. Resolution is
230
    // not truly finished until the last parallel query has been destroyed.
231
70
    if (pending_resolutions_ > 0) {
232
      return;
233
    }
234

            
235
70
    ASSERT(owned_);
236
    // This destruction might have been triggered by a peer PendingResolution that received a
237
    // ARES_ECONNREFUSED. If the PendingResolution has not been cancelled that means that the
238
    // callback_ target _should_ still be around. In that case, raise the callback_ so the target
239
    // can be done with this query and initiate a new one.
240
70
    ENVOY_LOG_EVENT(trace, "cares_dns_resolution_destroyed", "dns resolution for {} destroyed",
241
70
                    dns_name_);
242

            
243
    // Nothing can follow a call to finishResolve due to the deletion of this object upon
244
    // finishResolve().
245
70
    finishResolve();
246
70
    return;
247
70
  }
248

            
249
162
  if (!dual_resolution_) {
250
114
    completed_ = true;
251

            
252
    // If c-ares returns ARES_ECONNREFUSED and there is no fallback we assume that the channel_ is
253
    // broken and hence we reinitialize it here.
254
114
    if (status == ARES_ECONNREFUSED || status == ARES_EREFUSED || status == ARES_ESERVFAIL ||
255
114
        status == ARES_ENOTIMP || (status == ARES_ETIMEOUT && parent_.reinit_channel_on_timeout_)) {
256
4
      parent_.reinitializeChannel();
257
4
    }
258
114
  }
259

            
260
162
  if (status == ARES_SUCCESS) {
261
101
    pending_response_.status_ = ResolutionStatus::Completed;
262
101
    pending_response_.details_ = absl::StrCat("cares_success:", ares_strerror(status));
263

            
264
101
    if (addrinfo != nullptr && addrinfo->nodes != nullptr) {
265
101
      bool can_process_v4 =
266
101
          (!parent_.filter_unroutable_families_ || available_interfaces_.v4_available_);
267
101
      bool can_process_v6 =
268
101
          (!parent_.filter_unroutable_families_ || available_interfaces_.v6_available_);
269

            
270
101
      int32_t min_ttl = std::numeric_limits<
271
101
          int32_t>::max(); // [RFC 2181](https://datatracker.ietf.org/doc/html/rfc2181)
272
      // Loop through CNAME and get min_ttl
273
106
      for (const ares_addrinfo_cname* cname = addrinfo->cnames; cname != nullptr;
274
101
           cname = cname->next) {
275
5
        min_ttl = std::min(min_ttl, cname->ttl);
276
5
      }
277

            
278
366
      for (const ares_addrinfo_node* ai = addrinfo->nodes; ai != nullptr; ai = ai->ai_next) {
279
265
        if (ai->ai_family == AF_INET && can_process_v4) {
280
224
          sockaddr_in address;
281
224
          memset(&address, 0, sizeof(address));
282
224
          address.sin_family = AF_INET;
283
224
          address.sin_port = 0;
284
224
          address.sin_addr = reinterpret_cast<sockaddr_in*>(ai->ai_addr)->sin_addr;
285

            
286
224
          pending_response_.address_list_.emplace_back(
287
224
              DnsResponse(std::make_shared<const Address::Ipv4Instance>(&address),
288
224
                          std::chrono::seconds(std::min(min_ttl, ai->ai_ttl))));
289
224
        } else if (ai->ai_family == AF_INET6 && can_process_v6) {
290
39
          sockaddr_in6 address;
291
39
          memset(&address, 0, sizeof(address));
292
39
          address.sin6_family = AF_INET6;
293
39
          address.sin6_port = 0;
294
39
          address.sin6_addr = reinterpret_cast<sockaddr_in6*>(ai->ai_addr)->sin6_addr;
295
39
          pending_response_.address_list_.emplace_back(
296
39
              DnsResponse(std::make_shared<const Address::Ipv6Instance>(address),
297
39
                          std::chrono::seconds(std::min(min_ttl, ai->ai_ttl))));
298
39
        }
299
265
      }
300
101
    }
301

            
302
101
    if (!pending_response_.address_list_.empty() && dns_lookup_family_ != DnsLookupFamily::All) {
303
85
      completed_ = true;
304
85
    }
305

            
306
101
    ASSERT(addrinfo != nullptr);
307
101
    ares_freeaddrinfo(addrinfo);
308
101
  } else if (isResponseWithNoRecords(status)) {
309
    // Treat `ARES_ENODATA` or `ARES_ENOTFOUND` here as success to populate back the
310
    // "empty records" response.
311
37
    pending_response_.status_ = ResolutionStatus::Completed;
312
37
    pending_response_.details_ = absl::StrCat("cares_norecords:", ares_strerror(status));
313
37
    ASSERT(addrinfo == nullptr);
314
38
  } else {
315
24
    pending_response_.details_ = absl::StrCat("cares_failure:", ares_strerror(status));
316
24
  }
317

            
318
162
  if (timeouts > 0) {
319
1
    ENVOY_LOG(debug, "DNS request timed out {} times", timeouts);
320
1
  }
321

            
322
162
  if (completed_) {
323
135
    finishResolve();
324
    // Nothing can follow a call to finishResolve due to the deletion of this object upon
325
    // finishResolve().
326
135
    return;
327
135
  }
328

            
329
27
  if (dual_resolution_) {
330
27
    dual_resolution_ = false;
331

            
332
    // Perform a second lookup for DnsLookupFamily::Auto and DnsLookupFamily::V4Preferred, given
333
    // that the first lookup failed to return any addresses. Note that DnsLookupFamily::All issues
334
    // both lookups concurrently so there is no need to fire a second lookup here.
335
27
    if (dns_lookup_family_ == DnsLookupFamily::Auto) {
336
19
      family_ = AF_INET;
337
19
      startResolutionImpl(AF_INET);
338
19
    } else if (dns_lookup_family_ == DnsLookupFamily::V4Preferred) {
339
8
      family_ = AF_INET6;
340
8
      startResolutionImpl(AF_INET6);
341
8
    }
342

            
343
    // Note: Nothing can follow this call to getAddrInfo due to deletion of this
344
    // object upon synchronous resolution.
345
27
    return;
346
27
  }
347
27
}
348

            
349
205
void DnsResolverImpl::PendingResolution::finishResolve() {
350
205
  ENVOY_LOG_EVENT(trace, "cares_dns_resolution_complete",
351
205
                  "dns resolution for {} completed with status {:#06x}: \"{}\"", dns_name_,
352
205
                  static_cast<int>(pending_response_.status_), pending_response_.details_);
353

            
354
205
  if (!cancelled_) {
355
    // Use a raw try here because it is used in both main thread and filter.
356
    // Can not convert to use status code as there may be unexpected exceptions in server fuzz
357
    // tests, which must be handled. Potential exception may come from getAddressWithPort() or
358
    // portFromTcpUrl().
359
    // TODO(chaoqin-li1123): remove try catch pattern here once we figure how to handle unexpected
360
    // exception in fuzz tests.
361
148
    TRY_NEEDS_AUDIT {
362
148
      callback_(pending_response_.status_, std::move(pending_response_.details_),
363
148
                std::move(pending_response_.address_list_));
364
148
    }
365
148
    END_TRY
366
148
    MULTI_CATCH(
367
148
        const EnvoyException& e,
368
148
        {
369
148
          ENVOY_LOG(critical, "EnvoyException in c-ares callback: {}", e.what());
370
148
          dispatcher_.post([s = std::string(e.what())] { throw EnvoyException(s); });
371
148
        },
372
148
        {
373
148
          ENVOY_LOG(critical, "Unknown exception in c-ares callback");
374
148
          dispatcher_.post([] { throw EnvoyException("unknown"); });
375
148
        });
376
189
  } else {
377
57
    ENVOY_LOG_EVENT(debug, "cares_dns_callback_cancelled",
378
57
                    "dns resolution callback for {} not issued. Cancelled with reason={}",
379
57
                    dns_name_, static_cast<int>(cancel_reason_));
380
57
  }
381
205
  if (owned_) {
382
194
    delete this;
383
194
    return;
384
194
  }
385
205
}
386

            
387
929
void DnsResolverImpl::updateAresTimer() {
388
  // Update the timeout for events.
389
929
  timeval timeout;
390
929
  timeval* timeout_result = ares_timeout(channel_, nullptr, &timeout);
391
929
  if (timeout_result != nullptr) {
392
417
    const auto ms =
393
417
        std::chrono::milliseconds(timeout_result->tv_sec * 1000 + timeout_result->tv_usec / 1000);
394
417
    ENVOY_LOG(trace, "Setting DNS resolution timer for {} milliseconds", ms.count());
395
417
    timer_->enableTimer(ms);
396
532
  } else {
397
512
    timer_->disableTimer();
398
512
  }
399
929
}
400

            
401
249
void DnsResolverImpl::onEventCallback(os_fd_t fd, uint32_t events) {
402
249
  const ares_socket_t read_fd = events & Event::FileReadyType::Read ? fd : ARES_SOCKET_BAD;
403
249
  const ares_socket_t write_fd = events & Event::FileReadyType::Write ? fd : ARES_SOCKET_BAD;
404
249
  ares_process_fd(channel_, read_fd, write_fd);
405
249
  updateAresTimer();
406
249
}
407

            
408
486
void DnsResolverImpl::onAresSocketStateChange(os_fd_t fd, int read, int write) {
409
486
  updateAresTimer();
410
486
  auto it = events_.find(fd);
411
  // Stop tracking events for fd if no more state change events.
412
486
  if (read == 0 && write == 0) {
413
196
    if (it != events_.end()) {
414
196
      events_.erase(it);
415
196
    }
416
196
    return;
417
196
  }
418

            
419
  // If we weren't tracking the fd before, create a new FileEvent.
420
290
  if (it == events_.end()) {
421
196
    events_[fd] = dispatcher_.createFileEvent(
422
196
        fd,
423
305
        [this, fd](uint32_t events) {
424
246
          onEventCallback(fd, events);
425
246
          return absl::OkStatus();
426
246
        },
427
196
        Event::FileTriggerType::Level, Event::FileReadyType::Read | Event::FileReadyType::Write);
428
196
  }
429
290
  events_[fd]->setEnabled((read ? Event::FileReadyType::Read : 0) |
430
290
                          (write ? Event::FileReadyType::Write : 0));
431
290
}
432

            
433
12
void DnsResolverImpl::reinitializeChannel() {
434
12
  AresOptions options = defaultAresOptions();
435

            
436
  // Set up the options for re-initialization
437
12
  options.options_.sock_state_cb = [](void* arg, os_fd_t fd, int read, int write) {
438
    static_cast<DnsResolverImpl*>(arg)->onAresSocketStateChange(fd, read, write);
439
  };
440
12
  options.options_.sock_state_cb_data = this;
441
12
  options.optmask_ |= ARES_OPT_SOCK_STATE_CB;
442

            
443
  // Reinitialize the channel with the new options
444
12
  int result = ares_reinit(channel_);
445
12
  RELEASE_ASSERT(result == ARES_SUCCESS, "c-ares channel re-initialization failed");
446
12
  stats_.reinits_.inc();
447
12
  ENVOY_LOG_EVENT(trace, "cares_channel_reinitialized",
448
12
                  "Reinitialized cares channel via ares_reinit");
449

            
450
12
  if (resolvers_csv_.has_value()) {
451
9
    bool use_resolvers = true;
452
    // If the only name server available is c-ares' default then fallback to the user defined
453
    // resolvers. Otherwise, use the resolvers provided by c-ares.
454
9
    if (use_resolvers_as_fallback_ && !isCaresDefaultTheOnlyNameserver()) {
455
      use_resolvers = false;
456
    }
457

            
458
9
    if (use_resolvers) {
459
9
      result = ares_set_servers_ports_csv(channel_, resolvers_csv_->c_str());
460
9
      RELEASE_ASSERT(result == ARES_SUCCESS, "c-ares set servers failed during re-initialization");
461
9
    }
462
9
  }
463
12
}
464

            
465
7
void DnsResolverImpl::onUdpChannelRefreshTimer() {
466
7
  ENVOY_LOG_EVENT(debug, "cares_udp_channel_periodic_refresh",
467
7
                  "Performing periodic UDP channel refresh after {} ms",
468
7
                  max_udp_channel_duration_.count());
469

            
470
  // Reinitialize the channel to refresh UDP sockets.
471
7
  reinitializeChannel();
472

            
473
  // Re-enable the timer for the next periodic refresh.
474
7
  if (udp_channel_refresh_timer_) {
475
7
    udp_channel_refresh_timer_->enableTimer(max_udp_channel_duration_);
476
7
  }
477
7
}
478

            
479
ActiveDnsQuery* DnsResolverImpl::resolve(const std::string& dns_name,
480
205
                                         DnsLookupFamily dns_lookup_family, ResolveCb callback) {
481
205
  ENVOY_LOG_EVENT(trace, "cares_dns_resolution_start", "dns resolution for {} started", dns_name);
482

            
483
  // TODO(hennna): Add DNS caching which will allow testing the edge case of a
484
  // failed initial call to getAddrInfo followed by a synchronous IPv4
485
  // resolution.
486

            
487
205
  auto pending_resolution = std::make_unique<AddrInfoPendingResolution>(
488
205
      *this, callback, dispatcher_, channel_, dns_name, dns_lookup_family);
489
205
  pending_resolution->startResolution();
490
205
  if (pending_resolution->completed_) {
491
    // Resolution does not need asynchronous behavior or network events. For
492
    // example, localhost lookup.
493
11
    ENVOY_LOG_EVENT(trace, "cares_resolution_completed",
494
11
                    "dns resolution for {} completed with no async or network events", dns_name);
495
11
    return nullptr;
496
197
  } else {
497
    // Enable timer to wake us up if the request times out.
498
194
    updateAresTimer();
499

            
500
    // The PendingResolution will self-delete when the request completes (including if cancelled or
501
    // if ~DnsResolverImpl() happens via ares_destroy() and subsequent handling of ARES_EDESTRUCTION
502
    // in DnsResolverImpl::PendingResolution::onAresGetAddrInfoCallback()).
503
194
    pending_resolution->owned_ = true;
504
194
    return pending_resolution.release();
505
194
  }
506
205
}
507

            
508
131
void DnsResolverImpl::chargeGetAddrInfoErrorStats(int status, int timeouts) {
509
131
  switch (status) {
510
1
  case ARES_ENODATA:
511
1
    ABSL_FALLTHROUGH_INTENDED;
512
37
  case ARES_ENOTFOUND:
513
37
    stats_.not_found_.inc();
514
37
    break;
515
1
  case ARES_ETIMEOUT:
516
1
    stats_.timeouts_.add(timeouts);
517
1
    break;
518
93
  default:
519
93
    stats_.get_addr_failure_.inc();
520
131
  }
521
131
}
522

            
523
DnsResolverImpl::AddrInfoPendingResolution::AddrInfoPendingResolution(
524
    DnsResolverImpl& parent, ResolveCb callback, Event::Dispatcher& dispatcher,
525
    ares_channel channel, const std::string& dns_name, DnsLookupFamily dns_lookup_family)
526
205
    : PendingResolution(parent, callback, dispatcher, channel, dns_name),
527
205
      dns_lookup_family_(dns_lookup_family), available_interfaces_(availableInterfaces()) {
528
205
  if (dns_lookup_family == DnsLookupFamily::Auto ||
529
205
      dns_lookup_family == DnsLookupFamily::V4Preferred) {
530
82
    dual_resolution_ = true;
531
82
  }
532

            
533
205
  switch (dns_lookup_family_) {
534
87
  case DnsLookupFamily::V4Only:
535
103
  case DnsLookupFamily::V4Preferred:
536
103
    family_ = AF_INET;
537
103
    break;
538
11
  case DnsLookupFamily::V6Only:
539
77
  case DnsLookupFamily::Auto:
540
77
    family_ = AF_INET6;
541
77
    break;
542
25
  case DnsLookupFamily::All:
543
25
    family_ = AF_UNSPEC;
544
25
    break;
545
205
  }
546
205
}
547

            
548
205
DnsResolverImpl::AddrInfoPendingResolution::~AddrInfoPendingResolution() {
549
  // All pending resolutions should be cleaned up at this point.
550
205
  ASSERT(pending_resolutions_ == 0);
551
205
}
552

            
553
205
void DnsResolverImpl::AddrInfoPendingResolution::startResolution() { startResolutionImpl(family_); }
554

            
555
232
void DnsResolverImpl::AddrInfoPendingResolution::startResolutionImpl(int family) {
556
232
  pending_resolutions_++;
557
232
  parent_.stats_.pending_resolutions_.inc();
558
232
  if (parent_.filter_unroutable_families_ && family != AF_UNSPEC) {
559
12
    switch (family) {
560
6
    case AF_INET:
561
6
      if (!available_interfaces_.v4_available_) {
562
3
        ENVOY_LOG_EVENT(trace, "cares_resolution_filtered", "filtered v4 lookup");
563
3
        onAresGetAddrInfoCallback(ARES_EBADFAMILY, 0, nullptr);
564
3
        return;
565
3
      }
566
3
      break;
567
6
    case AF_INET6:
568
6
      if (!available_interfaces_.v6_available_) {
569
3
        ENVOY_LOG_EVENT(trace, "cares_resolution_filtered", "filtered v6 lookup");
570
3
        onAresGetAddrInfoCallback(ARES_EBADFAMILY, 0, nullptr);
571
3
        return;
572
3
      }
573
3
      break;
574
3
    default:
575
      ENVOY_BUG(false, fmt::format("Unexpected IP family {}", family));
576
12
    }
577
12
  }
578

            
579
226
  struct ares_addrinfo_hints hints = {};
580
226
  hints.ai_family = family;
581

            
582
  /**
583
   * ARES_AI_NOSORT result addresses will not be sorted and no connections to resolved addresses
584
   * will be attempted
585
   */
586
226
  hints.ai_flags = ARES_AI_NOSORT;
587

            
588
226
  ares_getaddrinfo(
589
226
      channel_, dns_name_.c_str(), /* service */ nullptr, &hints,
590
226
      [](void* arg, int status, int timeouts, ares_addrinfo* addrinfo) {
591
226
        static_cast<AddrInfoPendingResolution*>(arg)->onAresGetAddrInfoCallback(status, timeouts,
592
226
                                                                                addrinfo);
593
226
      },
594
226
      this);
595
226
}
596

            
597
DnsResolverImpl::AddrInfoPendingResolution::AvailableInterfaces
598
205
DnsResolverImpl::AddrInfoPendingResolution::availableInterfaces() {
599
205
  if (!Api::OsSysCallsSingleton::get().supportsGetifaddrs()) {
600
    // Maintain no-op behavior if the system cannot provide interface information.
601
1
    return {true, true};
602
1
  }
603

            
604
204
  if (!parent_.filter_unroutable_families_) {
605
193
    return {true, true};
606
193
  }
607

            
608
11
  Api::InterfaceAddressVector interface_addresses{};
609
11
  const Api::SysCallIntResult rc = Api::OsSysCallsSingleton::get().getifaddrs(interface_addresses);
610
11
  if (rc.return_value_ != 0) {
611
1
    ENVOY_LOG_EVENT(debug, "cares_getifaddrs_error",
612
1
                    "dns resolution for {} could not obtain interface information with error={}",
613
1
                    dns_name_, rc.errno_);
614
    // Maintain no-op behavior if the system encountered an error while providing interface
615
    // information.
616
1
    return {true, true};
617
1
  }
618

            
619
10
  DnsResolverImpl::AddrInfoPendingResolution::AvailableInterfaces available_interfaces{false,
620
10
                                                                                       false};
621
14
  for (const auto& interface_address : interface_addresses) {
622
14
    if (!interface_address.interface_addr_->ip()) {
623
      continue;
624
    }
625

            
626
14
    if (Network::Utility::isLoopbackAddress(*interface_address.interface_addr_)) {
627
2
      continue;
628
2
    }
629

            
630
12
    switch (interface_address.interface_addr_->ip()->version()) {
631
6
    case Network::Address::IpVersion::v4:
632
6
      available_interfaces.v4_available_ = true;
633
6
      if (available_interfaces.v6_available_) {
634
        return available_interfaces;
635
      }
636
6
      break;
637
6
    case Network::Address::IpVersion::v6:
638
6
      available_interfaces.v6_available_ = true;
639
6
      if (available_interfaces.v4_available_) {
640
3
        return available_interfaces;
641
3
      }
642
3
      break;
643
12
    }
644
12
  }
645
7
  return available_interfaces;
646
10
}
647

            
648
// c-ares DNS resolver factory
649
class CaresDnsResolverFactory : public DnsResolverFactory,
650
                                public Logger::Loggable<Logger::Id::dns> {
651
public:
652
194
  std::string name() const override { return std::string(CaresDnsResolver); }
653

            
654
80
  ProtobufTypes::MessagePtr createEmptyConfigProto() override {
655
80
    return ProtobufTypes::MessagePtr{
656
80
        new envoy::extensions::network::dns_resolver::cares::v3::CaresDnsResolverConfig()};
657
80
  }
658

            
659
  absl::StatusOr<DnsResolverSharedPtr>
660
  createDnsResolver(Event::Dispatcher& dispatcher, Api::Api& api,
661
                    const envoy::config::core::v3::TypedExtensionConfig& typed_dns_resolver_config)
662
174
      const override {
663
174
    envoy::extensions::network::dns_resolver::cares::v3::CaresDnsResolverConfig cares;
664
174
    std::vector<Network::Address::InstanceConstSharedPtr> resolvers;
665

            
666
174
    ASSERT(dispatcher.isThreadSafe());
667
    // Only c-ares DNS factory will call into this function.
668
    // Directly unpack the typed config to a c-ares object.
669
174
    RETURN_IF_NOT_OK(Envoy::MessageUtil::unpackTo(typed_dns_resolver_config.typed_config(), cares));
670
174
    if (!cares.resolvers().empty()) {
671
8
      const auto& resolver_addrs = cares.resolvers();
672
8
      resolvers.reserve(resolver_addrs.size());
673
14
      for (const auto& resolver_addr : resolver_addrs) {
674
14
        auto address_or_error = Network::Address::resolveProtoAddress(resolver_addr);
675
14
        RETURN_IF_NOT_OK_REF(address_or_error.status());
676
14
        resolvers.push_back(std::move(address_or_error.value()));
677
14
      }
678
8
    }
679
174
    auto csv_or_error = DnsResolverImpl::maybeBuildResolversCsv(resolvers);
680
174
    RETURN_IF_NOT_OK(csv_or_error.status());
681
173
    return std::make_shared<Network::DnsResolverImpl>(cares, dispatcher, csv_or_error.value(),
682
173
                                                      api.rootScope());
683
174
  }
684

            
685
177
  void initialize() override {
686
    // Initialize c-ares library in case first time.
687
177
    absl::MutexLock lock(mutex_);
688
177
    if (!ares_library_initialized_) {
689
9
      ares_library_initialized_ = true;
690
9
      ENVOY_LOG(trace, "c-ares library initialized.");
691
9
      ares_library_init(ARES_LIB_INIT_ALL);
692
9
    }
693
177
  }
694
12
  void terminate() override {
695
    // Cleanup c-ares library if initialized.
696
12
    absl::MutexLock lock(mutex_);
697
12
    if (ares_library_initialized_) {
698
9
      ares_library_initialized_ = false;
699
9
      ENVOY_LOG(trace, "c-ares library cleaned up.");
700
9
      ares_library_cleanup();
701
9
    }
702
12
  }
703

            
704
private:
705
  bool ares_library_initialized_ ABSL_GUARDED_BY(mutex_){false};
706
  absl::Mutex mutex_;
707
};
708

            
709
// Register the CaresDnsResolverFactory
710
REGISTER_FACTORY(CaresDnsResolverFactory, DnsResolverFactory);
711

            
712
} // namespace Network
713
} // namespace Envoy