1
#pragma once
2

            
3
#include "envoy/common/backoff_strategy.h"
4
#include "envoy/common/key_value_store.h"
5
#include "envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.pb.h"
6
#include "envoy/http/filter.h"
7
#include "envoy/network/dns.h"
8
#include "envoy/server/factory_context.h"
9
#include "envoy/thread_local/thread_local.h"
10

            
11
#include "source/common/common/cleanup.h"
12
#include "source/extensions/common/dynamic_forward_proxy/dns_cache.h"
13
#include "source/extensions/common/dynamic_forward_proxy/dns_cache_resource_manager.h"
14
#include "source/server/generic_factory_context.h"
15

            
16
#include "absl/container/flat_hash_map.h"
17

            
18
namespace Envoy {
19
namespace Extensions {
20
namespace Common {
21
namespace DynamicForwardProxy {
22

            
23
/**
24
 * All DNS cache stats. @see stats_macros.h
25
 */
26
#define ALL_DNS_CACHE_STATS(COUNTER, GAUGE)                                                        \
27
168
  COUNTER(cache_load)                                                                              \
28
168
  COUNTER(dns_query_attempt)                                                                       \
29
168
  COUNTER(dns_query_failure)                                                                       \
30
168
  COUNTER(dns_query_success)                                                                       \
31
168
  COUNTER(dns_query_timeout)                                                                       \
32
168
  COUNTER(host_added)                                                                              \
33
168
  COUNTER(host_address_changed)                                                                    \
34
168
  COUNTER(host_overflow)                                                                           \
35
168
  COUNTER(host_removed)                                                                            \
36
168
  COUNTER(dns_rq_pending_overflow)                                                                 \
37
168
  GAUGE(num_hosts, NeverImport)
38

            
39
/**
40
 * Struct definition for all DNS cache stats. @see stats_macros.h
41
 */
42
struct DnsCacheStats {
43
  ALL_DNS_CACHE_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT)
44
};
45

            
46
class DnsCacheImplTest;
47

            
48
class DnsCacheImpl : public DnsCache, Logger::Loggable<Logger::Id::forward_proxy> {
49
public:
50
  // Create a DnsCacheImpl or return a failed status;
51
  static absl::StatusOr<std::shared_ptr<DnsCacheImpl>> createDnsCacheImpl(
52
      Server::Configuration::GenericFactoryContext& context,
53
      const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig& config);
54

            
55
  ~DnsCacheImpl() override;
56
  static DnsCacheStats generateDnsCacheStats(Stats::Scope& scope);
57
  static absl::StatusOr<Network::DnsResolverSharedPtr> selectDnsResolver(
58
      const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig& config,
59
      Event::Dispatcher& main_thread_dispatcher,
60
      Server::Configuration::CommonFactoryContext& context);
61

            
62
  // DnsCache
63
  LoadDnsCacheEntryResult
64
  loadDnsCacheEntryWithForceRefresh(absl::string_view host, uint16_t default_port,
65
                                    bool is_proxy_lookup, bool force_refresh,
66
                                    LoadDnsCacheEntryCallbacks& callbacks) override;
67
  AddUpdateCallbacksHandlePtr addUpdateCallbacks(UpdateCallbacks& callbacks) override;
68
  void iterateHostMap(IterateHostMapCb cb) override;
69
  absl::optional<const DnsHostInfoSharedPtr> getHost(absl::string_view host_name) override;
70
  Upstream::ResourceAutoIncDecPtr canCreateDnsRequest() override;
71
  void forceRefreshHosts() override;
72
  void setIpVersionToRemove(absl::optional<Network::Address::IpVersion> ip_version) override;
73
  absl::optional<Network::Address::IpVersion> getIpVersionToRemove() override;
74
  void stop() override;
75

            
76
private:
77
  DnsCacheImpl(Server::Configuration::GenericFactoryContext& context,
78
               const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig& config,
79
               Network::DnsResolverSharedPtr&& resolver);
80
  struct LoadDnsCacheEntryHandleImpl
81
      : public LoadDnsCacheEntryHandle,
82
        RaiiMapOfListElement<std::string, LoadDnsCacheEntryHandleImpl*> {
83
    LoadDnsCacheEntryHandleImpl(
84
        absl::flat_hash_map<std::string, std::list<LoadDnsCacheEntryHandleImpl*>>& parent,
85
        absl::string_view host, LoadDnsCacheEntryCallbacks& callbacks)
86
122
        : RaiiMapOfListElement<std::string, LoadDnsCacheEntryHandleImpl*>(parent, host, this),
87
122
          callbacks_(callbacks) {}
88

            
89
    LoadDnsCacheEntryCallbacks& callbacks_;
90
  };
91

            
92
  class DnsHostInfoImpl;
93
  using DnsHostInfoImplSharedPtr = std::shared_ptr<DnsHostInfoImpl>;
94

            
95
  struct HostMapUpdateInfo {
96
    HostMapUpdateInfo(const std::string& host, DnsHostInfoImplSharedPtr info)
97
144
        : host_(host), info_(std::move(info)) {}
98
    std::string host_;
99
    DnsHostInfoImplSharedPtr info_;
100
  };
101
  using HostMapUpdateInfoSharedPtr = std::shared_ptr<HostMapUpdateInfo>;
102

            
103
  // Per-thread DNS cache info including pending callbacks.
104
  struct ThreadLocalHostInfo : public ThreadLocal::ThreadLocalObject {
105
271
    ThreadLocalHostInfo(DnsCacheImpl& parent) : parent_{parent} {}
106
    ~ThreadLocalHostInfo() override;
107
    void onHostMapUpdate(const HostMapUpdateInfoSharedPtr& resolved_info);
108
    absl::flat_hash_map<std::string, std::list<LoadDnsCacheEntryHandleImpl*>> pending_resolutions_;
109
    DnsCacheImpl& parent_;
110
  };
111

            
112
  class DnsHostInfoImpl : public DnsHostInfo {
113
  public:
114
    DnsHostInfoImpl(DnsCacheImpl& parent, absl::string_view resolved_host, bool is_ip_address);
115
    // DnsHostInfo
116
    Network::Address::InstanceConstSharedPtr address() const override;
117
    std::vector<Network::Address::InstanceConstSharedPtr> addressList() const override;
118
    const std::string& resolvedHost() const override;
119
    bool isIpAddress() const override;
120
    void touch() final;
121
    void updateStale(MonotonicTime resolution_time, std::chrono::seconds ttl);
122
    bool isStale();
123
    void setAddresses(std::vector<Network::Address::InstanceConstSharedPtr>&& list,
124
                      absl::string_view details,
125
                      Network::DnsResolver::ResolutionStatus resolution_status);
126
    void setDetails(absl::string_view details);
127
    std::string details() override;
128
    std::chrono::steady_clock::duration lastUsedTime() const;
129
    bool firstResolveComplete() const override;
130
    void setFirstResolveComplete();
131
    void setResolutionStatus(Network::DnsResolver::ResolutionStatus resolution_status);
132
    Network::DnsResolver::ResolutionStatus resolutionStatus() const override;
133

            
134
  private:
135
    friend class DnsCacheImplTest;
136
    DnsCacheImpl& parent_;
137
    const std::string resolved_host_;
138
    const bool is_ip_address_;
139
    mutable absl::Mutex resolve_lock_;
140
    std::vector<Network::Address::InstanceConstSharedPtr>
141
        address_list_ ABSL_GUARDED_BY(resolve_lock_);
142
    std::string details_ ABSL_GUARDED_BY(resolve_lock_){"not_resolved"};
143
    Network::DnsResolver::ResolutionStatus resolution_status_ ABSL_GUARDED_BY(resolve_lock_);
144

            
145
    // Using std::chrono::steady_clock::duration is required for compilation within an atomic vs.
146
    // using MonotonicTime.
147
    std::atomic<std::chrono::steady_clock::duration> last_used_time_;
148
    std::atomic<MonotonicTime> stale_at_time_;
149
    bool first_resolve_complete_ ABSL_GUARDED_BY(resolve_lock_){false};
150
  };
151

            
152
  // Primary host information that accounts for TTL, re-resolution, etc.
153
  struct PrimaryHostInfo {
154
    PrimaryHostInfo(DnsCacheImpl& parent, absl::string_view host_to_resolve, uint16_t port,
155
                    bool is_ip_address, const Event::TimerCb& refresh_timer_cb,
156
                    const Event::TimerCb& timeout_timer_cb);
157
    ~PrimaryHostInfo();
158

            
159
    DnsCacheImpl& parent_;
160
    const uint16_t port_;
161
    const Event::TimerPtr refresh_timer_;
162
    const Event::TimerPtr timeout_timer_;
163
    const DnsHostInfoImplSharedPtr host_info_;
164
    const BackOffStrategyPtr failure_backoff_strategy_;
165
    Network::ActiveDnsQuery* active_query_{};
166
  };
167

            
168
  // Hold PrimaryHostInfo by shared_ptr to avoid having to hold the map mutex while updating
169
  // individual entries.
170
  using PrimaryHostInfoPtr = std::unique_ptr<PrimaryHostInfo>;
171

            
172
  struct AddUpdateCallbacksHandleImpl : public AddUpdateCallbacksHandle,
173
                                        RaiiListElement<AddUpdateCallbacksHandleImpl*> {
174
    AddUpdateCallbacksHandleImpl(std::list<AddUpdateCallbacksHandleImpl*>& parent,
175
                                 UpdateCallbacks& callbacks)
176
158
        : RaiiListElement<AddUpdateCallbacksHandleImpl*>(parent, this), callbacks_(callbacks) {}
177

            
178
    UpdateCallbacks& callbacks_;
179
  };
180

            
181
  void startCacheLoad(const std::string& host, uint16_t default_port, bool is_proxy_lookup,
182
                      bool disallow_cached_results);
183

            
184
  void startResolve(const std::string& host, PrimaryHostInfo& host_info)
185
      ABSL_LOCKS_EXCLUDED(primary_hosts_lock_);
186

            
187
  void finishResolve(const std::string& host, Network::DnsResolver::ResolutionStatus status,
188
                     absl::string_view details, std::list<Network::DnsResponse>&& response,
189
                     absl::optional<MonotonicTime> resolution_time = {},
190
                     bool is_proxy_lookup = false, bool is_timeout = false);
191
  absl::Status runAddUpdateCallbacks(const std::string& host,
192
                                     const DnsHostInfoSharedPtr& host_info);
193
  void runResolutionCompleteCallbacks(const std::string& host,
194
                                      const DnsHostInfoSharedPtr& host_info,
195
                                      Network::DnsResolver::ResolutionStatus status);
196
  void runRemoveCallbacks(const std::string& host);
197
  void notifyThreads(const std::string& host, const DnsHostInfoImplSharedPtr& resolved_info);
198
  void onReResolveAlarm(const std::string& host);
199
  void removeHost(const std::string& host, const PrimaryHostInfo& host_info, bool update_threads);
200
  void onResolveTimeout(const std::string& host);
201
  PrimaryHostInfo& getPrimaryHost(const std::string& host);
202

            
203
  void addCacheEntry(const std::string& host,
204
                     const std::vector<Network::Address::InstanceConstSharedPtr>& address_list,
205
                     const std::chrono::seconds ttl);
206
  void removeCacheEntry(const std::string& host);
207
  void loadCacheEntries(
208
      const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig& config);
209
  PrimaryHostInfo* createHost(const std::string& host, uint16_t default_port);
210
  absl::optional<Network::DnsResponse> parseValue(absl::string_view value,
211
                                                  absl::optional<MonotonicTime>& resolution_time);
212

            
213
  Event::Dispatcher& main_thread_dispatcher_;
214
  const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig config_;
215
  Random::RandomGenerator& random_generator_;
216
  const Network::DnsLookupFamily dns_lookup_family_;
217
  const Network::DnsResolverSharedPtr resolver_;
218
  ThreadLocal::TypedSlot<ThreadLocalHostInfo> tls_slot_;
219
  Stats::ScopeSharedPtr scope_;
220
  DnsCacheStats stats_;
221
  std::list<AddUpdateCallbacksHandleImpl*> update_callbacks_;
222
  absl::Mutex primary_hosts_lock_;
223
  absl::flat_hash_map<std::string, PrimaryHostInfoPtr>
224
      primary_hosts_ ABSL_GUARDED_BY(primary_hosts_lock_);
225
  std::unique_ptr<KeyValueStore> key_value_store_;
226
  DnsCacheResourceManagerImpl resource_manager_;
227
  const std::chrono::milliseconds refresh_interval_;
228
  const std::chrono::milliseconds min_refresh_interval_;
229
  const std::chrono::milliseconds timeout_interval_;
230
  Filesystem::Instance& file_system_;
231
  ProtobufMessage::ValidationVisitor& validation_visitor_;
232
  const std::chrono::milliseconds host_ttl_;
233
  const uint32_t max_hosts_;
234
  absl::Mutex ip_version_to_remove_lock_;
235
  absl::optional<Network::Address::IpVersion>
236
      ip_version_to_remove_ ABSL_GUARDED_BY(ip_version_to_remove_lock_) = absl::nullopt;
237
  bool enable_dfp_dns_trace_;
238
};
239

            
240
} // namespace DynamicForwardProxy
241
} // namespace Common
242
} // namespace Extensions
243
} // namespace Envoy