Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/extensions/filters/udp/dns_filter/dns_filter.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/extensions/filters/udp/dns_filter/dns_filter.h"
2
3
#include "envoy/network/listener.h"
4
#include "envoy/type/matcher/v3/string.pb.h"
5
6
#include "source/common/config/datasource.h"
7
#include "source/common/network/address_impl.h"
8
#include "source/common/network/dns_resolver/dns_factory_util.h"
9
#include "source/common/protobuf/message_validator_impl.h"
10
#include "source/extensions/filters/udp/dns_filter/dns_filter_utils.h"
11
12
namespace Envoy {
13
namespace Extensions {
14
namespace UdpFilters {
15
namespace DnsFilter {
16
17
static constexpr std::chrono::milliseconds DEFAULT_RESOLVER_TIMEOUT{500};
18
static constexpr std::chrono::seconds DEFAULT_RESOLVER_TTL{300};
19
20
DnsFilterEnvoyConfig::DnsFilterEnvoyConfig(
21
    Server::Configuration::ListenerFactoryContext& context,
22
    const envoy::extensions::filters::udp::dns_filter::v3::DnsFilterConfig& config)
23
    : root_scope_(context.scope()), cluster_manager_(context.clusterManager()), api_(context.api()),
24
      stats_(generateStats(config.stat_prefix(), root_scope_)),
25
0
      resolver_timeout_(DEFAULT_RESOLVER_TIMEOUT), random_(context.api().randomGenerator()) {
26
0
  using envoy::extensions::filters::udp::dns_filter::v3::DnsFilterConfig;
27
28
0
  const auto& server_config = config.server_config();
29
30
0
  envoy::data::dns::v3::DnsTable dns_table;
31
0
  bool result = loadServerConfig(server_config, dns_table);
32
0
  ENVOY_LOG(debug, "Loading DNS table from external file: {}", result ? "Success" : "Failure");
33
34
0
  retry_count_ = dns_table.external_retry_count();
35
36
0
  for (const auto& virtual_domain : dns_table.virtual_domains()) {
37
0
    AddressConstPtrVec addrs{};
38
39
0
    const absl::string_view domain_name = virtual_domain.name();
40
0
    const absl::string_view suffix = Utils::getDomainSuffix(domain_name);
41
0
    ENVOY_LOG(trace, "Loading configuration for domain: {}. Suffix: {}", domain_name, suffix);
42
43
0
    if (virtual_domain.endpoint().has_address_list()) {
44
0
      const auto& address_list = virtual_domain.endpoint().address_list().address();
45
0
      addrs.reserve(address_list.size());
46
47
      // Shuffle the configured addresses. We store the addresses starting at a random
48
      // list index so that we do not always return answers in the same order as the IPs
49
      // are configured.
50
0
      size_t i = random_.random();
51
52
      // Creating the IP address will throw an exception if the address string is malformed
53
0
      for (auto index = 0; index < address_list.size(); index++) {
54
0
        const auto address_iter = std::next(address_list.begin(), (i++ % address_list.size()));
55
0
        auto ipaddr = Network::Utility::parseInternetAddress(*address_iter, 0 /* port */);
56
0
        addrs.push_back(std::move(ipaddr));
57
0
      }
58
59
0
      DnsEndpointConfig endpoint_config{};
60
61
      // Check whether the trie contains an entry for this domain
62
0
      auto virtual_domains = dns_lookup_trie_.find(suffix);
63
0
      if (virtual_domains != nullptr) {
64
        // The suffix already has a node in the trie
65
66
0
        auto existing_endpoint_config = virtual_domains->find(domain_name);
67
0
        if (existing_endpoint_config != virtual_domains->end()) {
68
          // Update the existing endpoint config with the new addresses
69
70
0
          auto& addr_vec = existing_endpoint_config->second.address_list.value();
71
0
          addr_vec.reserve(addr_vec.size() + addrs.size());
72
0
          std::move(addrs.begin(), addrs.end(), std::inserter(addr_vec, addr_vec.end()));
73
0
        } else {
74
          // Add a new endpoint config for the new domain
75
0
          endpoint_config.address_list = absl::make_optional<AddressConstPtrVec>(std::move(addrs));
76
0
          virtual_domains->emplace(std::string(domain_name), std::move(endpoint_config));
77
0
        }
78
0
      } else {
79
0
        endpoint_config.address_list = absl::make_optional<AddressConstPtrVec>(std::move(addrs));
80
0
        addEndpointToSuffix(suffix, domain_name, endpoint_config);
81
0
      }
82
0
    }
83
84
0
    if (virtual_domain.endpoint().has_service_list()) {
85
0
      const auto& dns_service_list = virtual_domain.endpoint().service_list();
86
0
      for (const auto& dns_service : dns_service_list.services()) {
87
88
        // Each service should be its own domain in the stored config. The filter will see
89
        // the full service name in queries on the wire. The protocol string returned will be empty
90
        // if a numeric protocol is configured and we cannot resolve its name
91
0
        const std::string proto = Utils::getProtoName(dns_service.protocol());
92
0
        if (proto.empty()) {
93
0
          continue;
94
0
        }
95
0
        const std::chrono::seconds ttl = std::chrono::seconds(dns_service.ttl().seconds());
96
97
        // Generate the full name for the DNS service. All input parameters are populated
98
        // strings enforced by the message definition
99
0
        const std::string full_service_name =
100
0
            Utils::buildServiceName(dns_service.service_name(), proto, virtual_domain.name());
101
102
0
        DnsSrvRecordPtr service_record_ptr =
103
0
            std::make_unique<DnsSrvRecord>(full_service_name, proto, ttl);
104
105
        // Store service targets. We require at least one target to be present. The target should
106
        // be a fully qualified domain name. If the target name is not a fully qualified name, we
107
        // will consider this name to be that of a cluster
108
0
        for (const auto& target : dns_service.targets()) {
109
0
          DnsSrvRecord::DnsTargetAttributes attributes{};
110
0
          attributes.priority = target.priority();
111
0
          attributes.weight = target.weight();
112
0
          attributes.port = target.port();
113
114
0
          absl::string_view target_name = target.host_name();
115
0
          if (target_name.empty()) {
116
0
            target_name = target.cluster_name();
117
0
            attributes.is_cluster = true;
118
0
          }
119
120
0
          ENVOY_LOG(trace, "Storing service {} target {}", full_service_name, target_name);
121
0
          service_record_ptr->addTarget(target_name, attributes);
122
0
        }
123
124
0
        DnsEndpointConfig endpoint_config{};
125
0
        endpoint_config.service_list =
126
0
            absl::make_optional<DnsSrvRecordPtr>(std::move(service_record_ptr));
127
128
0
        auto virtual_domains = dns_lookup_trie_.find(suffix);
129
0
        if (virtual_domains != nullptr) {
130
0
          virtual_domains->emplace(full_service_name, std::move(endpoint_config));
131
0
        }
132
0
      }
133
0
    }
134
135
    // A DNS name can be redirected to only one cluster.
136
0
    const absl::string_view cluster_name = virtual_domain.endpoint().cluster_name();
137
0
    if (!cluster_name.empty()) {
138
0
      DnsEndpointConfig endpoint_config{};
139
0
      endpoint_config.cluster_name = absl::make_optional<std::string>(cluster_name);
140
141
      // See if there's a suffix already configured
142
0
      auto virtual_domains = dns_lookup_trie_.find(suffix);
143
0
      if (virtual_domains == nullptr) {
144
0
        addEndpointToSuffix(suffix, domain_name, endpoint_config);
145
0
      } else {
146
        // A domain can be redirected to one cluster. If it appears multiple times, the first
147
        // entry is the only one used
148
0
        if (virtual_domains->find(domain_name) == virtual_domains->end()) {
149
0
          virtual_domains->emplace(domain_name, std::move(endpoint_config));
150
0
        }
151
0
      }
152
0
    }
153
154
0
    std::chrono::seconds ttl = virtual_domain.has_answer_ttl()
155
0
                                   ? std::chrono::seconds(virtual_domain.answer_ttl().seconds())
156
0
                                   : DEFAULT_RESOLVER_TTL;
157
0
    domain_ttl_.emplace(virtual_domain.name(), ttl);
158
0
  }
159
160
0
  forward_queries_ = config.has_client_config();
161
0
  if (forward_queries_) {
162
0
    const auto& client_config = config.client_config();
163
0
    dns_resolver_factory_ =
164
0
        &Network::createDnsResolverFactoryFromProto(client_config, typed_dns_resolver_config_);
165
    // Set additional resolving options from configuration
166
0
    resolver_timeout_ = std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(
167
0
        client_config, resolver_timeout, DEFAULT_RESOLVER_TIMEOUT.count()));
168
0
    max_pending_lookups_ = client_config.max_pending_lookups();
169
0
  } else {
170
    // In case client_config doesn't exist, create default DNS resolver factory and save it.
171
0
    dns_resolver_factory_ = &Network::createDefaultDnsResolverFactory(typed_dns_resolver_config_);
172
0
    max_pending_lookups_ = 0;
173
0
  }
174
0
}
175
176
void DnsFilterEnvoyConfig::addEndpointToSuffix(const absl::string_view suffix,
177
                                               const absl::string_view domain_name,
178
0
                                               DnsEndpointConfig& endpoint_config) {
179
180
0
  DnsVirtualDomainConfigSharedPtr virtual_domains = std::make_shared<DnsVirtualDomainConfig>();
181
0
  virtual_domains->emplace(std::string(domain_name), std::move(endpoint_config));
182
183
0
  auto success = dns_lookup_trie_.add(suffix, std::move(virtual_domains), false);
184
0
  ASSERT(success, "Unable to overwrite existing suffix in dns_filter trie");
185
0
}
186
187
bool DnsFilterEnvoyConfig::loadServerConfig(
188
    const envoy::extensions::filters::udp::dns_filter::v3::DnsFilterConfig::ServerContextConfig&
189
        config,
190
0
    envoy::data::dns::v3::DnsTable& table) {
191
0
  using envoy::data::dns::v3::DnsTable;
192
193
0
  if (config.has_inline_dns_table()) {
194
0
    table = config.inline_dns_table();
195
0
    return true;
196
0
  }
197
198
0
  const auto& datasource = config.external_dns_table();
199
0
  bool data_source_loaded = false;
200
0
  TRY_NEEDS_AUDIT {
201
    // Data structure is deduced from the file extension. If the data is not read an exception
202
    // is thrown. If no table can be read, the filter will refer all queries to an external
203
    // DNS server, if configured, otherwise all queries will be responded to with Name Error.
204
0
    MessageUtil::loadFromFile(datasource.filename(), table,
205
0
                              ProtobufMessage::getNullValidationVisitor(), api_);
206
0
    data_source_loaded = true;
207
0
  }
208
0
  END_TRY catch (const ProtobufMessage::UnknownProtoFieldException& e) {
209
0
    ENVOY_LOG(warn, "Invalid field in DNS Filter datasource configuration: {}", e.what());
210
0
  }
211
0
  catch (const EnvoyException& e) {
212
0
    ENVOY_LOG(warn, "Filesystem DNS Filter config update failure: {}", e.what());
213
0
  }
214
0
  return data_source_loaded;
215
0
}
216
217
DnsFilter::DnsFilter(Network::UdpReadFilterCallbacks& callbacks,
218
                     const DnsFilterEnvoyConfigSharedPtr& config)
219
    : UdpListenerReadFilter(callbacks), config_(config), listener_(callbacks.udpListener()),
220
      cluster_manager_(config_->clusterManager()),
221
      message_parser_(config->forwardQueries(), listener_.dispatcher().timeSource(),
222
                      config->retryCount(), config->random(),
223
0
                      config_->stats().downstream_rx_query_latency_) {
224
  // This callback is executed when the dns resolution completes. At that time of a response by
225
  // the resolver, we build an answer record from each IP returned then send a response to the
226
  // client
227
0
  resolver_callback_ = [this](DnsQueryContextPtr context, const DnsQueryRecord* query,
228
0
                              AddressConstPtrVec& iplist) -> void {
229
    // We cannot retry the resolution if ares returns without a response. The ares context
230
    // is still dirty and will result in a segfault when it is freed during a subsequent resolve
231
    // call from here. We will retry resolutions for pending lookups only
232
0
    if (context->resolution_status_ != Network::DnsResolver::ResolutionStatus::Success &&
233
0
        !context->in_callback_ && context->retry_ > 0) {
234
0
      --context->retry_;
235
0
      ENVOY_LOG(debug, "resolving name [{}] via external resolvers [retry {}]", query->name_,
236
0
                context->retry_);
237
0
      resolver_->resolveExternalQuery(std::move(context), query);
238
0
      return;
239
0
    }
240
241
0
    config_->stats().externally_resolved_queries_.inc();
242
0
    if (iplist.empty()) {
243
0
      config_->stats().unanswered_queries_.inc();
244
0
    }
245
246
0
    incrementExternalQueryTypeCount(query->type_);
247
0
    for (const auto& ip : iplist) {
248
0
      incrementExternalQueryTypeAnswerCount(query->type_);
249
0
      const std::chrono::seconds ttl = getDomainTTL(query->name_);
250
0
      message_parser_.storeDnsAnswerRecord(context, *query, ttl, std::move(ip));
251
0
    }
252
0
    sendDnsResponse(std::move(context));
253
0
  };
254
255
0
  resolver_ = std::make_unique<DnsFilterResolver>(
256
0
      resolver_callback_, config->resolverTimeout(), listener_.dispatcher(),
257
0
      config->maxPendingLookups(), config->typedDnsResolverConfig(), config->dnsResolverFactory(),
258
0
      config->api());
259
0
}
260
261
0
Network::FilterStatus DnsFilter::onData(Network::UdpRecvData& client_request) {
262
0
  config_->stats().downstream_rx_bytes_.recordValue(client_request.buffer_->length());
263
0
  config_->stats().downstream_rx_queries_.inc();
264
265
  // Setup counters for the parser
266
0
  DnsParserCounters parser_counters(
267
0
      config_->stats().query_buffer_underflow_, config_->stats().record_name_overflow_,
268
0
      config_->stats().query_parsing_failure_, config_->stats().queries_with_additional_rrs_,
269
0
      config_->stats().queries_with_ans_or_authority_rrs_);
270
271
  // Parse the query, if it fails return an response to the client
272
0
  DnsQueryContextPtr query_context =
273
0
      message_parser_.createQueryContext(client_request, parser_counters);
274
0
  incrementQueryTypeCount(query_context->queries_);
275
0
  if (!query_context->parse_status_) {
276
0
    config_->stats().downstream_rx_invalid_queries_.inc();
277
0
    sendDnsResponse(std::move(query_context));
278
0
    return Network::FilterStatus::StopIteration;
279
0
  }
280
281
  // Resolve the requested name and respond to the client. If the return code is
282
  // External, we will respond to the client when the upstream resolver returns
283
0
  if (getResponseForQuery(query_context) == DnsLookupResponseCode::External) {
284
0
    return Network::FilterStatus::StopIteration;
285
0
  }
286
287
  // We have an answer, it might be "No Answer". Send it to the client
288
0
  sendDnsResponse(std::move(query_context));
289
290
0
  return Network::FilterStatus::StopIteration;
291
0
}
292
293
0
void DnsFilter::sendDnsResponse(DnsQueryContextPtr query_context) {
294
0
  Buffer::OwnedImpl response;
295
296
  // Serializes the generated response to the parsed query from the client. If there is a
297
  // parsing error or the incoming query is invalid, we will still generate a valid DNS response
298
0
  message_parser_.buildResponseBuffer(query_context, response);
299
0
  config_->stats().downstream_tx_responses_.inc();
300
0
  config_->stats().downstream_tx_bytes_.recordValue(response.length());
301
0
  Network::UdpSendData response_data{query_context->local_->ip(), *(query_context->peer_),
302
0
                                     response};
303
0
  listener_.send(response_data);
304
0
}
305
306
0
DnsLookupResponseCode DnsFilter::getResponseForQuery(DnsQueryContextPtr& context) {
307
  /* It appears to be a rare case where we would have more than one query in a single request.
308
   * It is allowed by the protocol but not widely supported:
309
   *
310
   * See: https://www.ietf.org/rfc/rfc1035.txt
311
   *
312
   * The question section is used to carry the "question" in most queries,
313
   * i.e., the parameters that define what is being asked. The section
314
   * contains QDCOUNT (usually 1) entries.
315
   */
316
0
  for (const auto& query : context->queries_) {
317
    // Try to resolve the query locally. If forwarding the query externally is disabled we will
318
    // always attempt to resolve with the configured domains
319
0
    const bool forward_queries = config_->forwardQueries();
320
0
    if (isKnownDomain(query->name_) || !forward_queries) {
321
      // Determine whether the name is a cluster. Move on to the next query if successful
322
0
      if (resolveViaClusters(context, *query)) {
323
0
        continue;
324
0
      }
325
326
      // Determine whether we an answer this query with the static configuration
327
0
      if (resolveViaConfiguredHosts(context, *query)) {
328
0
        continue;
329
0
      }
330
0
    }
331
332
    // Forwarding queries is enabled if the configuration contains a client configuration
333
    // for the dns_filter.
334
0
    if (forward_queries) {
335
0
      ENVOY_LOG(debug, "resolving name [{}] via external resolvers", query->name_);
336
0
      resolver_->resolveExternalQuery(std::move(context), query.get());
337
338
0
      return DnsLookupResponseCode::External;
339
0
    }
340
0
  }
341
342
0
  if (context->answers_.empty()) {
343
0
    config_->stats().unanswered_queries_.inc();
344
0
    return DnsLookupResponseCode::Failure;
345
0
  }
346
0
  return DnsLookupResponseCode::Success;
347
0
}
348
349
bool DnsFilter::resolveViaConfiguredHosts(DnsQueryContextPtr& context,
350
0
                                          const DnsQueryRecord& query) {
351
0
  switch (query.type_) {
352
0
  case DNS_RECORD_TYPE_A:
353
0
  case DNS_RECORD_TYPE_AAAA:
354
0
    return resolveConfiguredDomain(context, query);
355
0
  case DNS_RECORD_TYPE_SRV:
356
0
    return resolveConfiguredService(context, query);
357
0
  default:
358
0
    return false;
359
0
  }
360
0
}
361
362
0
std::chrono::seconds DnsFilter::getDomainTTL(const absl::string_view domain) {
363
0
  const auto& domain_ttl_config = config_->domainTtl();
364
0
  const auto& iter = domain_ttl_config.find(domain);
365
366
0
  if (iter == domain_ttl_config.end()) {
367
0
    return DEFAULT_RESOLVER_TTL;
368
0
  }
369
0
  return iter->second;
370
0
}
371
372
0
bool DnsFilter::isKnownDomain(const absl::string_view domain_name) {
373
0
  const absl::string_view suffix = Utils::getDomainSuffix(domain_name);
374
0
  auto config = config_->getDnsTrie().find(suffix);
375
376
0
  if (config != nullptr) {
377
0
    config_->stats().known_domain_queries_.inc();
378
0
    return true;
379
0
  }
380
381
0
  return false;
382
0
}
383
384
0
const DnsEndpointConfig* DnsFilter::getEndpointConfigForDomain(const absl::string_view domain) {
385
0
  const absl::string_view suffix = Utils::getDomainSuffix(domain);
386
0
  const auto virtual_domains = config_->getDnsTrie().find(suffix);
387
388
0
  if (virtual_domains == nullptr) {
389
0
    ENVOY_LOG(debug, "No domain configuration exists for [{}]", domain);
390
0
    return nullptr;
391
0
  }
392
393
0
  const auto iter = virtual_domains->find(domain);
394
0
  if (iter == virtual_domains->end()) {
395
0
    ENVOY_LOG(debug, "No endpoint configuration exists for [{}]", domain);
396
0
    return nullptr;
397
0
  }
398
0
  return &(iter->second);
399
0
}
400
401
0
const DnsSrvRecord* DnsFilter::getServiceConfigForDomain(const absl::string_view domain) {
402
0
  const DnsEndpointConfig* endpoint_config = getEndpointConfigForDomain(domain);
403
0
  if (endpoint_config != nullptr && endpoint_config->service_list.has_value()) {
404
0
    return endpoint_config->service_list.value().get();
405
0
  }
406
0
  return nullptr;
407
0
}
408
409
0
const AddressConstPtrVec* DnsFilter::getAddressListForDomain(const absl::string_view domain) {
410
0
  const DnsEndpointConfig* endpoint_config = getEndpointConfigForDomain(domain);
411
0
  if (endpoint_config != nullptr && endpoint_config->address_list.has_value()) {
412
0
    return &(endpoint_config->address_list.value());
413
0
  }
414
0
  return nullptr;
415
0
}
416
417
0
const absl::string_view DnsFilter::getClusterNameForDomain(const absl::string_view domain) {
418
0
  const DnsEndpointConfig* endpoint_config = getEndpointConfigForDomain(domain);
419
0
  if (endpoint_config != nullptr && endpoint_config->cluster_name.has_value()) {
420
0
    return endpoint_config->cluster_name.value();
421
0
  }
422
0
  return {};
423
0
}
424
425
0
bool DnsFilter::resolveClusterService(DnsQueryContextPtr& context, const DnsQueryRecord& query) {
426
0
  size_t cluster_endpoints = 0;
427
428
  // Get the service_list config for the domain
429
0
  const auto* service_config = getServiceConfigForDomain(query.name_);
430
0
  if (service_config != nullptr) {
431
    // We can redirect to more than one cluster, but only one is supported
432
0
    const auto& cluster_target = service_config->targets_.begin();
433
0
    const auto& target_name = cluster_target->first;
434
0
    const auto& attributes = cluster_target->second;
435
436
0
    if (!attributes.is_cluster) {
437
0
      ENVOY_LOG(trace, "Service target [{}] is not a cluster", target_name);
438
0
      return false;
439
0
    }
440
441
    // Determine if there is a cluster
442
0
    Upstream::ThreadLocalCluster* cluster = cluster_manager_.getThreadLocalCluster(target_name);
443
0
    if (cluster == nullptr) {
444
0
      ENVOY_LOG(trace, "No cluster found for service target: {}", target_name);
445
0
      return false;
446
0
    }
447
448
    // Add a service record for each cluster endpoint using the cluster name
449
0
    const std::chrono::seconds ttl = getDomainTTL(target_name);
450
0
    for (const auto& hostsets : cluster->prioritySet().hostSetsPerPriority()) {
451
0
      for (const auto& host : hostsets->hosts()) {
452
453
        // If the target port is zero, use the port from the cluster host.
454
        // If the cluster host port is zero also, then this is the value that will
455
        // appear in the service record. Zero is a permitted value in the record
456
0
        DnsSrvRecord::DnsTargetAttributes new_attributes = attributes;
457
0
        if (!new_attributes.port) {
458
0
          new_attributes.port = host->address()->ip()->port();
459
0
        }
460
461
        // Create the service record element and increment the SRV record answer count
462
0
        auto config = std::make_unique<DnsSrvRecord>(service_config->name_, service_config->proto_,
463
0
                                                     service_config->ttl_);
464
465
0
        config->addTarget(target_name, new_attributes);
466
0
        message_parser_.storeDnsSrvAnswerRecord(context, query, std::move(config));
467
0
        incrementClusterQueryTypeAnswerCount(query.type_);
468
469
        // Return the address for all discovered endpoints
470
0
        ENVOY_LOG(debug, "using host address {} for cluster [{}]",
471
0
                  host->address()->ip()->addressAsString(), target_name);
472
473
        // We have to determine the address type here so that we increment the correct counter
474
0
        const auto type = Utils::getAddressRecordType(host->address());
475
0
        if (type.has_value() &&
476
0
            message_parser_.storeDnsAdditionalRecord(context, target_name, type.value(),
477
0
                                                     query.class_, ttl, host->address())) {
478
0
          ++cluster_endpoints;
479
0
          incrementClusterQueryTypeAnswerCount(type.value());
480
0
        }
481
0
      }
482
0
    }
483
0
  }
484
0
  return (cluster_endpoints != 0);
485
0
}
486
487
0
bool DnsFilter::resolveClusterHost(DnsQueryContextPtr& context, const DnsQueryRecord& query) {
488
  // Determine if the domain name is being redirected to a cluster
489
0
  const auto cluster_name = getClusterNameForDomain(query.name_);
490
0
  absl::string_view lookup_name;
491
0
  if (!cluster_name.empty()) {
492
0
    lookup_name = cluster_name;
493
0
  } else {
494
0
    lookup_name = query.name_;
495
0
  }
496
497
  // Return an address for all discovered endpoints. The address and query type must match
498
  // for the host to be included in the response
499
0
  size_t cluster_endpoints = 0;
500
0
  Upstream::ThreadLocalCluster* cluster = cluster_manager_.getThreadLocalCluster(lookup_name);
501
0
  if (cluster != nullptr) {
502
    // TODO(abaptiste): consider using host weights when returning answer addresses
503
0
    const std::chrono::seconds ttl = getDomainTTL(lookup_name);
504
505
0
    for (const auto& hostsets : cluster->prioritySet().hostSetsPerPriority()) {
506
0
      for (const auto& host : hostsets->hosts()) {
507
        // Return the address for all discovered endpoints
508
0
        ENVOY_LOG(debug, "using cluster host address {} for domain [{}]",
509
0
                  host->address()->ip()->addressAsString(), lookup_name);
510
0
        if (message_parser_.storeDnsAnswerRecord(context, query, ttl, host->address())) {
511
0
          incrementClusterQueryTypeAnswerCount(query.type_);
512
0
          ++cluster_endpoints;
513
0
        }
514
0
      }
515
0
    }
516
0
  }
517
0
  return (cluster_endpoints != 0);
518
0
}
519
520
0
bool DnsFilter::resolveViaClusters(DnsQueryContextPtr& context, const DnsQueryRecord& query) {
521
0
  switch (query.type_) {
522
0
  case DNS_RECORD_TYPE_SRV:
523
0
    return resolveClusterService(context, query);
524
0
  case DNS_RECORD_TYPE_A:
525
0
  case DNS_RECORD_TYPE_AAAA:
526
0
    return resolveClusterHost(context, query);
527
0
  default:
528
    // unsupported query type
529
0
    return false;
530
0
  }
531
0
}
532
533
0
bool DnsFilter::resolveConfiguredDomain(DnsQueryContextPtr& context, const DnsQueryRecord& query) {
534
0
  const auto* configured_address_list = getAddressListForDomain(query.name_);
535
0
  uint64_t hosts_found = 0;
536
0
  if (configured_address_list != nullptr) {
537
    // Build an answer record from each configured IP address
538
0
    for (const auto& configured_address : *configured_address_list) {
539
0
      ASSERT(configured_address != nullptr);
540
0
      ENVOY_LOG(trace, "using local address {} for domain [{}]",
541
0
                configured_address->ip()->addressAsString(), query.name_);
542
0
      ++hosts_found;
543
0
      const std::chrono::seconds ttl = getDomainTTL(query.name_);
544
0
      if (message_parser_.storeDnsAnswerRecord(context, query, ttl, configured_address)) {
545
0
        incrementLocalQueryTypeAnswerCount(query.type_);
546
0
      }
547
0
    }
548
0
  }
549
0
  return (hosts_found != 0);
550
0
}
551
552
0
bool DnsFilter::resolveConfiguredService(DnsQueryContextPtr& context, const DnsQueryRecord& query) {
553
0
  const auto* service_config = getServiceConfigForDomain(query.name_);
554
555
0
  size_t targets_discovered = 0;
556
0
  if (service_config != nullptr) {
557
    // for each service target address, we must resolve the target's IP. The target record does not
558
    // specify the address type, so we must deduce it when building the record. It is possible that
559
    // the configured target's IP addresses are a mix of A and AAAA records.
560
0
    for (const auto& [target_name, attributes] : service_config->targets_) {
561
0
      const auto* configured_address_list = getAddressListForDomain(target_name);
562
563
0
      if (configured_address_list != nullptr) {
564
        // Build an SRV answer record for the service. We need a new SRV record for each target.
565
        // Although the same class is used, the target storage is different than the way the service
566
        // config is modeled. We store one SrvRecord per target so that we can enforce the response
567
        // size limit when serializing the answers to the client
568
0
        ENVOY_LOG(trace, "Adding srv record for target [{}]", target_name);
569
570
0
        incrementLocalQueryTypeAnswerCount(query.type_);
571
0
        auto config = std::make_unique<DnsSrvRecord>(service_config->name_, service_config->proto_,
572
0
                                                     service_config->ttl_);
573
0
        config->addTarget(target_name, attributes);
574
0
        message_parser_.storeDnsSrvAnswerRecord(context, query, std::move(config));
575
576
0
        for (const auto& configured_address : *configured_address_list) {
577
0
          ASSERT(configured_address != nullptr);
578
579
          // Since there is no type, only a name, we must determine the record type from its address
580
0
          ENVOY_LOG(trace, "using address {} for target [{}] in SRV record",
581
0
                    configured_address->ip()->addressAsString(), target_name);
582
0
          const std::chrono::seconds ttl = getDomainTTL(target_name);
583
584
0
          const auto type = Utils::getAddressRecordType(configured_address);
585
0
          if (type.has_value()) {
586
0
            incrementLocalQueryTypeAnswerCount(type.value());
587
0
            message_parser_.storeDnsAdditionalRecord(context, target_name, type.value(),
588
0
                                                     query.class_, ttl, configured_address);
589
0
            ++targets_discovered;
590
0
          }
591
0
        }
592
0
      }
593
0
    }
594
0
  }
595
0
  return (targets_discovered != 0);
596
0
}
597
598
0
Network::FilterStatus DnsFilter::onReceiveError(Api::IoError::IoErrorCode error_code) {
599
0
  config_->stats().downstream_rx_errors_.inc();
600
0
  UNREFERENCED_PARAMETER(error_code);
601
602
0
  return Network::FilterStatus::StopIteration;
603
0
}
604
605
} // namespace DnsFilter
606
} // namespace UdpFilters
607
} // namespace Extensions
608
} // namespace Envoy