1
#pragma once
2

            
3
#include <cstdint>
4
#include <string>
5

            
6
#include "envoy/common/platform.h"
7
#include "envoy/event/dispatcher.h"
8
#include "envoy/event/file_event.h"
9
#include "envoy/network/dns.h"
10
#include "envoy/registry/registry.h"
11

            
12
#include "source/common/common/linked_object.h"
13
#include "source/common/common/logger.h"
14
#include "source/common/common/utility.h"
15
#include "source/common/network/dns_resolver/dns_factory_util.h"
16

            
17
#include "absl/container/node_hash_map.h"
18
#include "ares.h"
19

            
20
namespace Envoy {
21
namespace Network {
22

            
23
/**
24
 * All DNS stats. @see stats_macros.h
25
 */
26
#define ALL_CARES_DNS_RESOLVER_STATS(COUNTER, GAUGE)                                               \
27
178
  COUNTER(resolve_total)                                                                           \
28
178
  GAUGE(pending_resolutions, NeverImport)                                                          \
29
178
  COUNTER(not_found)                                                                               \
30
178
  COUNTER(get_addr_failure)                                                                        \
31
178
  COUNTER(timeouts)                                                                                \
32
178
  COUNTER(reinits)
33

            
34
/**
35
 * Struct definition for all DNS stats. @see stats_macros.h
36
 */
37
struct CaresDnsResolverStats {
38
  ALL_CARES_DNS_RESOLVER_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT)
39
};
40

            
41
class DnsResolverImplPeer;
42

            
43
/**
44
 * Implementation of DnsResolver that uses c-ares. All calls and callbacks are assumed to
45
 * happen on the thread that owns the creating dispatcher.
46
 */
47
class DnsResolverImpl : public DnsResolver, protected Logger::Loggable<Logger::Id::dns> {
48
public:
49
  static absl::StatusOr<absl::optional<std::string>>
50
  maybeBuildResolversCsv(const std::vector<Network::Address::InstanceConstSharedPtr>& resolvers);
51

            
52
  DnsResolverImpl(
53
      const envoy::extensions::network::dns_resolver::cares::v3::CaresDnsResolverConfig& config,
54
      Event::Dispatcher& dispatcher, absl::optional<std::string> resolvers_csv,
55
      Stats::Scope& root_scope);
56
  ~DnsResolverImpl() override;
57

            
58
  static CaresDnsResolverStats generateCaresDnsResolverStats(Stats::Scope& scope);
59

            
60
  // Network::DnsResolver
61
  ActiveDnsQuery* resolve(const std::string& dns_name, DnsLookupFamily dns_lookup_family,
62
                          ResolveCb callback) override;
63
1
  void resetNetworking() override { reinitializeChannel(); }
64

            
65
private:
66
  friend class DnsResolverImplPeer;
67
  class PendingResolution : public ActiveDnsQuery {
68
  public:
69
    // Network::ActiveDnsQuery
70
57
    void cancel(CancelReason reason) override {
71
      // c-ares only supports channel-wide cancellation, so we just allow the
72
      // network events to continue but don't invoke the callback on completion.
73
      // TODO(mattklein123): Potentially use timeout to destroy and recreate the channel.
74
57
      cancelled_ = true;
75
57
      cancel_reason_ = reason;
76
57
    }
77
    void addTrace(uint8_t) override {}
78
    std::string getTraces() override { return {}; }
79

            
80
    // Does the object own itself? Resource reclamation occurs via self-deleting
81
    // on query completion or error.
82
    bool owned_ = false;
83
    // Has the query completed? Only meaningful if !owned_;
84
    bool completed_ = false;
85

            
86
  protected:
87
    // Network::ActiveDnsQuery
88
    PendingResolution(DnsResolverImpl& parent, ResolveCb callback, Event::Dispatcher& dispatcher,
89
                      ares_channel channel, const std::string& dns_name)
90
205
        : parent_(parent), callback_(callback), dispatcher_(dispatcher), channel_(channel),
91
205
          dns_name_(dns_name) {}
92

            
93
    void finishResolve();
94

            
95
    DnsResolverImpl& parent_;
96
    // Caller supplied callback to invoke on query completion or error.
97
    const ResolveCb callback_;
98
    // Dispatcher to post any callback_ exceptions to.
99
    Event::Dispatcher& dispatcher_;
100
    // Was the query cancelled via cancel()?
101
    bool cancelled_ = false;
102
    const ares_channel channel_;
103
    const std::string dns_name_;
104
    CancelReason cancel_reason_;
105

            
106
    // Small wrapping struct to accumulate addresses from firings of the
107
    // onAresGetAddrInfoCallback callback.
108
    struct PendingResponse {
109
      ResolutionStatus status_;
110
      std::list<DnsResponse> address_list_;
111
      std::string details_{"not_set"};
112
    };
113

            
114
    // Note: pending_response_ is constructed with ResolutionStatus::Failure by default and
115
    // __only__ changed to ResolutionStatus::Completed if there is an `ARES_SUCCESS`
116
    // or `ARES_ENODATA` or `ARES_ENOTFOUND`reply. In the dual_resolution case __any__ ARES_SUCCESS
117
    // reply will result in a ResolutionStatus::Completed callback.
118
    PendingResponse pending_response_{ResolutionStatus::Failure, {}};
119
  };
120

            
121
  class AddrInfoPendingResolution final : public PendingResolution {
122
  public:
123
    AddrInfoPendingResolution(DnsResolverImpl& parent, ResolveCb callback,
124
                              Event::Dispatcher& dispatcher, ares_channel channel,
125
                              const std::string& dns_name, DnsLookupFamily dns_lookup_family);
126
    ~AddrInfoPendingResolution() override;
127

            
128
    /**
129
     * ares_getaddrinfo query callback.
130
     * @param status return status of call to ares_getaddrinfo.
131
     * @param timeouts the number of times the request timed out.
132
     * @param addrinfo structure to store address info.
133
     */
134
    void onAresGetAddrInfoCallback(int status, int timeouts, ares_addrinfo* addrinfo);
135

            
136
    /**
137
     * wrapper function of call to ares_getaddrinfo.
138
     */
139
    void startResolution();
140

            
141
  private:
142
    void startResolutionImpl(int family);
143
    bool isResponseWithNoRecords(int status);
144

            
145
    // Holds the availability of non-loopback network interfaces for the system.
146
    struct AvailableInterfaces {
147
      bool v4_available_;
148
      bool v6_available_;
149
    };
150

            
151
    // Return the currently available network interfaces.
152
    // Note: this call uses syscalls.
153
    AvailableInterfaces availableInterfaces();
154

            
155
    // Perform a second resolution under certain conditions. If dns_lookup_family_ is V4Preferred
156
    // or Auto: perform a second resolution if the first one fails. If dns_lookup_family_ is All:
157
    // perform resolutions on both families concurrently.
158
    bool dual_resolution_ = false;
159
    // The number of outstanding pending resolutions. This should always be 0 or 1 for all lookup
160
    // types other than All. For all, this can be 0-2 as both queries are issued in parallel.
161
    // This is used mostly for assertions but is used in the ARES_EDESTRUCTION path to make sure
162
    // all concurrent queries are unwound before cleaning up the resolution.
163
    uint32_t pending_resolutions_ = 0;
164
    int family_ = AF_INET;
165
    const DnsLookupFamily dns_lookup_family_;
166
    // Queried for at construction time.
167
    const AvailableInterfaces available_interfaces_;
168
  };
169

            
170
  struct AresOptions {
171
    ares_options options_;
172
    int optmask_;
173
  };
174

            
175
  // Callback for events on sockets tracked in events_.
176
  void onEventCallback(os_fd_t fd, uint32_t events);
177
  // c-ares callback when a socket state changes, indicating that libevent
178
  // should listen for read/write events.
179
  void onAresSocketStateChange(os_fd_t fd, int read, int write);
180
  // Re-initialize the c-ares channel.
181
  void reinitializeChannel();
182
  // Initialize the channel.
183
  void initializeChannel(ares_options* options, int optmask);
184
  // Check if the only nameserver available is the c-ares default.
185
  bool isCaresDefaultTheOnlyNameserver();
186
  // Update timer for c-ares timeouts.
187
  void updateAresTimer();
188
  // Callback for periodic UDP channel refresh.
189
  void onUdpChannelRefreshTimer();
190
  // Return default AresOptions.
191
  AresOptions defaultAresOptions();
192

            
193
  void chargeGetAddrInfoErrorStats(int status, int timeouts);
194

            
195
  Event::Dispatcher& dispatcher_;
196
  Event::TimerPtr timer_;
197
  Event::TimerPtr udp_channel_refresh_timer_;
198
  ares_channel channel_;
199
  envoy::config::core::v3::DnsResolverOptions dns_resolver_options_;
200

            
201
  absl::node_hash_map<int, Event::FileEventPtr> events_;
202
  const bool use_resolvers_as_fallback_;
203
  const uint32_t udp_max_queries_;
204
  const uint64_t query_timeout_seconds_;
205
  const uint32_t query_tries_;
206
  const bool rotate_nameservers_;
207
  const uint32_t edns0_max_payload_size_;
208
  const std::chrono::milliseconds max_udp_channel_duration_;
209
  const bool reinit_channel_on_timeout_;
210
  const absl::optional<std::string> resolvers_csv_;
211
  const bool filter_unroutable_families_;
212
  Stats::ScopeSharedPtr scope_;
213
  CaresDnsResolverStats stats_;
214
};
215

            
216
DECLARE_FACTORY(CaresDnsResolverFactory);
217

            
218
} // namespace Network
219
} // namespace Envoy