1
#pragma once
2

            
3
#include "envoy/common/random_generator.h"
4
#include "envoy/event/dispatcher.h"
5
#include "envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.pb.h"
6
#include "envoy/singleton/manager.h"
7
#include "envoy/thread_local/thread_local.h"
8
#include "envoy/upstream/resource_manager.h"
9

            
10
#include "source/common/http/header_utility.h"
11
#include "source/common/runtime/runtime_features.h"
12

            
13
namespace Envoy {
14
namespace Extensions {
15
namespace Common {
16
namespace DynamicForwardProxy {
17

            
18
/**
19
 * A cached DNS host.
20
 */
21
class DnsHostInfo {
22
public:
23
  // DFP hosts are created after a DNS lookup for a domain name (e.g. foo.com) but are created
24
  // with an IP address and port to resolve to. To prevent bugs where we insert say foo.com
25
  // (default port 80) then later look up foo.com (default port 443) and get IP addresses with
26
  // port 80, we "fully qualify" hostnames with the port.
27
  //
28
  // This normalizes hostnames, respecting the port if it exists, and adding the default port
29
  // if there is no port.
30
312
  static std::string normalizeHostForDfp(absl::string_view host, uint16_t default_port) {
31
312
    if (Envoy::Http::HeaderUtility::hostHasPort(host)) {
32
178
      return std::string(host);
33
178
    }
34
134
    return absl::StrCat(host, ":", default_port);
35
312
  }
36

            
37
164
  virtual ~DnsHostInfo() = default;
38

            
39
  /**
40
   * Returns the host's currently resolved address. This address may change periodically due to
41
   * async re-resolution. This address may be null in the case of failed resolution.
42
   */
43
  virtual Network::Address::InstanceConstSharedPtr address() const PURE;
44

            
45
  /**
46
   * Returns whether the first DNS resolving attempt is completed or not.
47
   */
48
  virtual bool firstResolveComplete() const PURE;
49

            
50
  /**
51
   * Returns the host's currently resolved address. These addresses may change periodically due to
52
   * async re-resolution.
53
   */
54
  virtual std::vector<Network::Address::InstanceConstSharedPtr> addressList() const PURE;
55

            
56
  /**
57
   * Returns the host that was actually resolved via DNS. If port was originally specified it will
58
   * be stripped from this return value.
59
   */
60
  virtual const std::string& resolvedHost() const PURE;
61

            
62
  /**
63
   * Returns whether the original host is an IP address.
64
   */
65
  virtual bool isIpAddress() const PURE;
66

            
67
  /**
68
   * Indicates that the host has been used and should not be purged depending on any configured
69
   * TTL policy
70
   */
71
  virtual void touch() PURE;
72

            
73
  /**
74
   * Returns details about the resolution which resulted in the addresses above.
75
   * This includes both success and failure details.
76
   */
77
  virtual std::string details() PURE;
78

            
79
  /**
80
   * Returns the resolution status.
81
   */
82
  virtual Network::DnsResolver::ResolutionStatus resolutionStatus() const PURE;
83
};
84

            
85
using DnsHostInfoSharedPtr = std::shared_ptr<DnsHostInfo>;
86

            
87
#define ALL_DNS_CACHE_CIRCUIT_BREAKERS_STATS(OPEN_GAUGE, REMAINING_GAUGE)                          \
88
169
  OPEN_GAUGE(rq_pending_open, Accumulate)                                                          \
89
169
  REMAINING_GAUGE(rq_pending_remaining, Accumulate)
90

            
91
struct DnsCacheCircuitBreakersStats {
92
  ALL_DNS_CACHE_CIRCUIT_BREAKERS_STATS(GENERATE_GAUGE_STRUCT, GENERATE_GAUGE_STRUCT)
93
};
94

            
95
/**
96
 * A resource manager of DNS Cache.
97
 */
98
class DnsCacheResourceManager {
99
public:
100
169
  virtual ~DnsCacheResourceManager() = default;
101

            
102
  /**
103
   * Returns the resource limit of pending requests to DNS.
104
   */
105
  virtual ResourceLimit& pendingRequests() PURE;
106

            
107
  /**
108
   * Returns the reference of stats for dns cache circuit breakers.
109
   */
110
  virtual DnsCacheCircuitBreakersStats& stats() PURE;
111
};
112

            
113
/**
114
 * A cache of DNS hosts. Hosts will re-resolve their addresses or be automatically purged
115
 * depending on configured policy.
116
 */
117
class DnsCache {
118
public:
119
  /**
120
   * Callbacks used in the loadDnsCacheEntry() method.
121
   */
122
  class LoadDnsCacheEntryCallbacks {
123
  public:
124
363
    virtual ~LoadDnsCacheEntryCallbacks() = default;
125

            
126
    /**
127
     * Called when the DNS cache load is complete (or failed).
128
     *
129
     * @param host_info the DnsHostInfo for the resolved host.
130
     */
131
    virtual void onLoadDnsCacheComplete(const DnsHostInfoSharedPtr& host_info) PURE;
132
  };
133

            
134
  /**
135
   * Handle returned from loadDnsCacheEntry(). Destruction of the handle will cancel any future
136
   * callback.
137
   */
138
  class LoadDnsCacheEntryHandle {
139
  public:
140
153
    virtual ~LoadDnsCacheEntryHandle() = default;
141
  };
142

            
143
  using LoadDnsCacheEntryHandlePtr = std::unique_ptr<LoadDnsCacheEntryHandle>;
144

            
145
  /**
146
   * Update callbacks that can be registered in the addUpdateCallbacks() method.
147
   */
148
  class UpdateCallbacks {
149
  public:
150
177
    virtual ~UpdateCallbacks() = default;
151

            
152
    /**
153
     * Called when a host has been added or has had its address updated.
154
     * @param host supplies the added/updated host.
155
     * @param host_info supplies the associated host info.
156
     * @param return supplies if the host was successfully added
157
     */
158
    virtual absl::Status onDnsHostAddOrUpdate(const std::string& host,
159
                                              const DnsHostInfoSharedPtr& host_info) PURE;
160

            
161
    /**
162
     * Called when a host has been removed.
163
     * @param host supplies the removed host.
164
     */
165
    virtual void onDnsHostRemove(const std::string& host) PURE;
166

            
167
    /**
168
     * Called when any resolution for a host completes.
169
     * @param host supplies the added/updated host.
170
     * @param host_info supplies the associated host info.
171
     * @param status supplies the resolution status.
172
     */
173
    virtual void onDnsResolutionComplete(const std::string& host,
174
                                         const DnsHostInfoSharedPtr& host_info,
175
                                         Network::DnsResolver::ResolutionStatus status) PURE;
176
  };
177

            
178
  /**
179
   * Handle returned from addUpdateCallbacks(). Destruction of the handle will remove the
180
   * registered callbacks.
181
   */
182
  class AddUpdateCallbacksHandle {
183
  public:
184
158
    virtual ~AddUpdateCallbacksHandle() = default;
185
  };
186

            
187
  using AddUpdateCallbacksHandlePtr = std::unique_ptr<AddUpdateCallbacksHandle>;
188

            
189
295
  virtual ~DnsCache() = default;
190

            
191
  /**
192
   * Initiate a DNS cache load.
193
   * @param host supplies the host to load. Hosts are cached inclusive of port, even though the
194
   *             port will be stripped during resolution. This means that 'a.b.c' and 'a.b.c:9001'
195
   *             will both resolve 'a.b.c' but will generate different host entries with different
196
   *             target ports.
197
   * @param default_port supplies the port to use if the host does not have a port embedded in it.
198
   * @param callbacks supplies the cache load callbacks to invoke if async processing is needed.
199
   * @return a cache load result which includes both a status and handle. If the handle is non-null
200
   *         the callbacks will be invoked at a later time, otherwise consult the status for the
201
   *         reason the cache is not loading. In this case, callbacks will never be called.
202
   */
203
  enum class LoadDnsCacheEntryStatus {
204
    // The cache entry is already loaded. Callbacks will not be called.
205
    InCache,
206
    // The cache entry is loading. Callbacks will be called at a later time unless cancelled.
207
    Loading,
208
    // The cache is full and the requested host is not in cache. Callbacks will not be called.
209
    Overflow
210
  };
211

            
212
  struct LoadDnsCacheEntryResult {
213
    LoadDnsCacheEntryStatus status_;
214
    LoadDnsCacheEntryHandlePtr handle_;
215
    absl::optional<DnsHostInfoSharedPtr> host_info_;
216
  };
217

            
218
  /**
219
   * Legacy API to avoid churn while we determine if |force_refresh| below is useful.
220
   */
221
  virtual LoadDnsCacheEntryResult loadDnsCacheEntry(absl::string_view host, uint16_t default_port,
222
                                                    bool is_proxy_lookup,
223
97
                                                    LoadDnsCacheEntryCallbacks& callbacks) {
224
97
    return loadDnsCacheEntryWithForceRefresh(host, default_port, is_proxy_lookup, false, callbacks);
225
97
  }
226

            
227
  /**
228
   * Attempt to load a DNS cache entry.
229
   * @param host the hostname to lookup
230
   * @param default_port the port to use
231
   * @param is_proxy_lookup indicates if the request is safe to fast-fail. The Dynamic Forward Proxy
232
   * filter sets this to true if no address is necessary due to an upstream proxy being configured.
233
   * @param force_refresh forces a fresh DNS cache lookup if true.
234
   * @return a handle that on destruction will de-register the callbacks.
235
   */
236
  virtual LoadDnsCacheEntryResult
237
  loadDnsCacheEntryWithForceRefresh(absl::string_view host, uint16_t default_port,
238
                                    bool is_proxy_lookup, bool force_refresh,
239
                                    LoadDnsCacheEntryCallbacks& callbacks) PURE;
240

            
241
  /**
242
   * Add update callbacks to the cache.
243
   * @param callbacks supplies the callbacks to add.
244
   * @return a handle that on destruction will de-register the callbacks.
245
   */
246
  virtual AddUpdateCallbacksHandlePtr addUpdateCallbacks(UpdateCallbacks& callbacks) PURE;
247

            
248
  using IterateHostMapCb = std::function<void(absl::string_view, const DnsHostInfoSharedPtr&)>;
249

            
250
  /**
251
   * Iterates over all entries in the cache, calling a callback for each entry
252
   *
253
   * @param iterate_callback the callback to invoke for each entry in the cache
254
   */
255
  virtual void iterateHostMap(IterateHostMapCb iterate_callback) PURE;
256

            
257
  /**
258
   * Retrieve the DNS host info of a given host currently stored in the cache.
259
   * @param host_name supplies the host name.
260
   * @return the DNS host info associated with the given host name if the host's address is cached,
261
   * otherwise `absl::nullopt`.
262
   */
263
  virtual absl::optional<const DnsHostInfoSharedPtr> getHost(absl::string_view host_name) PURE;
264

            
265
  /**
266
   * Check if a DNS request is allowed given resource limits.
267
   * @return RAII handle for pending request circuit breaker if the request was allowed.
268
   */
269
  virtual Upstream::ResourceAutoIncDecPtr canCreateDnsRequest() PURE;
270

            
271
  /**
272
   * Force a DNS refresh of all known hosts, ignoring any ongoing failure or success timers. This
273
   * can be used in response to network changes which might alter DNS responses, for example.
274
   */
275
  virtual void forceRefreshHosts() PURE;
276

            
277
  /**
278
   * Sets the `IpVersion` addresses to be removed from the DNS response. This can be useful for a
279
   * use case where the DNS response returns both IPv4 and IPv6 and we are only interested a
280
   * specific IP version, we can save time not having to try to connect to both IPv4 and IPv6
281
   * addresses.
282
   */
283
  virtual void setIpVersionToRemove(absl::optional<Network::Address::IpVersion> ip_version) PURE;
284

            
285
  /**
286
   * Gets the `IpVersion` addresses to be removed from the DNS response.
287
   */
288
  virtual absl::optional<Network::Address::IpVersion> getIpVersionToRemove() PURE;
289

            
290
  /**
291
   * Stops the DNS cache background tasks by canceling the pending queries and stopping the timeout
292
   * and refresh timers. This function can be useful when the network is unavailable, such as when
293
   * a device is in airplane mode, etc.
294
   */
295
  virtual void stop() PURE;
296
};
297

            
298
using DnsCacheSharedPtr = std::shared_ptr<DnsCache>;
299

            
300
/**
301
 * A manager for multiple DNS caches.
302
 */
303
class DnsCacheManager {
304
public:
305
298
  virtual ~DnsCacheManager() = default;
306

            
307
  /**
308
   * Get a DNS cache.
309
   * @param config supplies the cache parameters. If a cache exists with the same parameters it
310
   *               will be returned, otherwise a new one will be created.
311
   */
312
  virtual absl::StatusOr<DnsCacheSharedPtr>
313
  getCache(const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig& config) PURE;
314

            
315
  /**
316
   * Look up an existing DNS cache by name.
317
   * @param name supplies the cache name to look up. If a cache exists with the same name it
318
   *             will be returned.
319
   * @return pointer to the cache if it exists, nullptr otherwise.
320
   */
321
  virtual DnsCacheSharedPtr lookUpCacheByName(absl::string_view cache_name) PURE;
322
};
323

            
324
using DnsCacheManagerSharedPtr = std::shared_ptr<DnsCacheManager>;
325

            
326
/**
327
 * Factory for getting a DNS cache manager.
328
 */
329
class DnsCacheManagerFactory {
330
public:
331
123
  virtual ~DnsCacheManagerFactory() = default;
332

            
333
  /**
334
   * Get a DNS cache manager.
335
   */
336
  virtual DnsCacheManagerSharedPtr get() PURE;
337
};
338

            
339
} // namespace DynamicForwardProxy
340
} // namespace Common
341
} // namespace Extensions
342
} // namespace Envoy