/proc/self/cwd/source/extensions/filters/udp/dns_filter/dns_filter_resolver.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "source/extensions/filters/udp/dns_filter/dns_filter_resolver.h" |
2 | | |
3 | | #include "source/common/network/utility.h" |
4 | | |
5 | | namespace Envoy { |
6 | | namespace Extensions { |
7 | | namespace UdpFilters { |
8 | | namespace DnsFilter { |
9 | | |
10 | | void DnsFilterResolver::resolveExternalQuery(DnsQueryContextPtr context, |
11 | 0 | const DnsQueryRecord* domain_query) { |
12 | | // Create an external resolution context for the query. |
13 | 0 | LookupContext ctx{}; |
14 | 0 | ctx.query_rec = domain_query; |
15 | 0 | ctx.query_context = std::move(context); |
16 | 0 | ctx.query_context->in_callback_ = false; |
17 | 0 | ctx.expiry = DateUtil::nowToSeconds(dispatcher_.timeSource()) + |
18 | 0 | std::chrono::duration_cast<std::chrono::seconds>(timeout_).count(); |
19 | 0 | ctx.resolver_status = DnsFilterResolverStatus::Pending; |
20 | |
|
21 | 0 | Network::DnsLookupFamily lookup_family; |
22 | 0 | switch (domain_query->type_) { |
23 | 0 | case DNS_RECORD_TYPE_A: |
24 | 0 | lookup_family = Network::DnsLookupFamily::V4Only; |
25 | 0 | break; |
26 | 0 | case DNS_RECORD_TYPE_AAAA: |
27 | 0 | lookup_family = Network::DnsLookupFamily::V6Only; |
28 | 0 | break; |
29 | 0 | default: |
30 | | // We don't support other lookups other than A and AAAA. Set success here so that we don't |
31 | | // retry for something that we are certain will fail. |
32 | 0 | ENVOY_LOG(debug, "Unknown query type [{}] for upstream lookup", domain_query->type_); |
33 | 0 | ctx.query_context->resolution_status_ = Network::DnsResolver::ResolutionStatus::Success; |
34 | 0 | ctx.resolver_status = DnsFilterResolverStatus::Complete; |
35 | 0 | invokeCallback(ctx); |
36 | 0 | return; |
37 | 0 | } |
38 | | |
39 | 0 | const DnsQueryRecord* id = domain_query; |
40 | | |
41 | | // If we have too many pending lookups, invoke the callback to retry the query. |
42 | 0 | if (lookups_.size() > max_pending_lookups_) { |
43 | 0 | ENVOY_LOG( |
44 | 0 | trace, |
45 | 0 | "Retrying query for [{}] because there are too many pending lookups: [pending {}/max {}]", |
46 | 0 | domain_query->name_, lookups_.size(), max_pending_lookups_); |
47 | 0 | ctx.resolver_status = DnsFilterResolverStatus::Complete; |
48 | 0 | invokeCallback(ctx); |
49 | 0 | return; |
50 | 0 | } |
51 | | |
52 | 0 | ctx.timeout_timer = dispatcher_.createTimer([this]() -> void { onResolveTimeout(); }); |
53 | 0 | ctx.timeout_timer->enableTimer(timeout_); |
54 | |
|
55 | 0 | lookups_.emplace(id, std::move(ctx)); |
56 | |
|
57 | 0 | ENVOY_LOG(trace, "Pending queries: {}", lookups_.size()); |
58 | | |
59 | | // Define the callback that is executed when resolution completes |
60 | | // Resolve the address in the query and add to the resolved_hosts vector |
61 | 0 | resolver_->resolve(domain_query->name_, lookup_family, |
62 | 0 | [this, id](Network::DnsResolver::ResolutionStatus status, |
63 | 0 | std::list<Network::DnsResponse>&& response) -> void { |
64 | 0 | auto ctx_iter = lookups_.find(id); |
65 | | |
66 | | // If the context is not in the map, the lookup has timed out and was removed |
67 | | // when the timer executed |
68 | 0 | if (ctx_iter == lookups_.end()) { |
69 | 0 | ENVOY_LOG(debug, "Unable to find context for DNS query for ID [{}]", |
70 | 0 | reinterpret_cast<intptr_t>(id)); |
71 | 0 | return; |
72 | 0 | } |
73 | | |
74 | 0 | auto ctx = std::move(ctx_iter->second); |
75 | 0 | lookups_.erase(ctx_iter->first); |
76 | | |
77 | | // We are processing the response here, so we did not timeout. Cancel the |
78 | | // timer |
79 | 0 | ctx.timeout_timer->disableTimer(); |
80 | |
|
81 | 0 | ENVOY_LOG(trace, "async query status returned for query [{}]. Entries {}", |
82 | 0 | ctx.query_context->id_, response.size()); |
83 | 0 | ASSERT(ctx.resolver_status == DnsFilterResolverStatus::Pending); |
84 | | |
85 | 0 | ctx.query_context->in_callback_ = true; |
86 | 0 | ctx.query_context->resolution_status_ = status; |
87 | 0 | ctx.resolver_status = DnsFilterResolverStatus::Complete; |
88 | | |
89 | | // C-ares doesn't expose the TTL in the data available here. |
90 | 0 | if (status == Network::DnsResolver::ResolutionStatus::Success) { |
91 | 0 | ctx.resolved_hosts.reserve(response.size()); |
92 | 0 | for (const auto& resp : response) { |
93 | 0 | const auto& addrinfo = resp.addrInfo(); |
94 | 0 | ASSERT(addrinfo.address_ != nullptr); |
95 | 0 | ENVOY_LOG(trace, "Resolved address: {} for {}", |
96 | 0 | addrinfo.address_->ip()->addressAsString(), |
97 | 0 | ctx.query_rec->name_); |
98 | 0 | ctx.resolved_hosts.emplace_back(std::move(addrinfo.address_)); |
99 | 0 | } |
100 | 0 | } |
101 | | // Invoke the filter callback notifying it of resolved addresses |
102 | 0 | invokeCallback(ctx); |
103 | 0 | }); |
104 | 0 | } |
105 | | |
106 | 0 | void DnsFilterResolver::onResolveTimeout() { |
107 | 0 | const uint64_t now = DateUtil::nowToSeconds(dispatcher_.timeSource()); |
108 | 0 | ENVOY_LOG(trace, "Pending queries: {}", lookups_.size()); |
109 | | |
110 | | // Find an outstanding pending query and purge it |
111 | 0 | for (auto& ctx_iter : lookups_) { |
112 | 0 | if (ctx_iter.second.expiry <= now && |
113 | 0 | ctx_iter.second.resolver_status == DnsFilterResolverStatus::Pending) { |
114 | 0 | auto ctx = std::move(ctx_iter.second); |
115 | |
|
116 | 0 | ENVOY_LOG(trace, "Purging expired query: {}", ctx_iter.first->name_); |
117 | |
|
118 | 0 | ctx.query_context->resolution_status_ = Network::DnsResolver::ResolutionStatus::Failure; |
119 | |
|
120 | 0 | lookups_.erase(ctx_iter.first); |
121 | 0 | callback_(std::move(ctx.query_context), ctx.query_rec, ctx.resolved_hosts); |
122 | 0 | return; |
123 | 0 | } |
124 | 0 | } |
125 | 0 | } |
126 | | } // namespace DnsFilter |
127 | | } // namespace UdpFilters |
128 | | } // namespace Extensions |
129 | | } // namespace Envoy |