1
#pragma once
2

            
3
#include "envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.pb.h"
4
#include "envoy/network/dns_resolver.h"
5
#include "envoy/registry/registry.h"
6

            
7
#include "source/common/api/os_sys_calls_impl.h"
8
#include "source/common/network/address_impl.h"
9

            
10
namespace Envoy {
11
namespace Network {
12

            
13
DECLARE_FACTORY(GetAddrInfoDnsResolverFactory);
14

            
15
// Trace information for getaddrinfo.
16
enum class GetAddrInfoTrace : uint8_t {
17
  NotStarted = 0,
18
  Starting = 1,
19
  Success = 2,
20
  Failed = 3,
21
  NoResult = 4,
22
  Retrying = 5,
23
  DoneRetrying = 6,
24
  Cancelled = 7,
25
  Callback = 8,
26
};
27

            
28
// This resolver uses getaddrinfo() on a dedicated resolution thread. Thus, it is only suitable
29
// currently for relatively low rate resolutions. In the future a thread pool could be added if
30
// desired.
31
class GetAddrInfoDnsResolver : public DnsResolver, public Logger::Loggable<Logger::Id::dns> {
32
public:
33
  GetAddrInfoDnsResolver(
34
      const envoy::extensions::network::dns_resolver::getaddrinfo::v3::GetAddrInfoDnsResolverConfig&
35
          config,
36
      Event::Dispatcher& dispatcher, Api::Api& api);
37

            
38
  ~GetAddrInfoDnsResolver() override;
39

            
40
  // DnsResolver
41
  ActiveDnsQuery* resolve(const std::string& dns_name, DnsLookupFamily dns_lookup_family,
42
                          ResolveCb callback) override;
43
26
  void resetNetworking() override {}
44

            
45
protected:
46
  class PendingQuery : public ActiveDnsQuery {
47
  public:
48
    PendingQuery(const std::string& dns_name, DnsLookupFamily dns_lookup_family, ResolveCb callback)
49
156
        : dns_name_(dns_name), dns_lookup_family_(dns_lookup_family),
50
156
          callback_(std::move(callback)) {}
51

            
52
11
    void cancel(CancelReason) override {
53
11
      ENVOY_LOG(trace, "cancelling query [{}]", dns_name_);
54
11
      absl::MutexLock lock(mutex_);
55
11
      cancelled_ = true;
56
11
    }
57

            
58
605
    void addTrace(uint8_t trace) override {
59
605
      absl::MutexLock lock(mutex_);
60
605
      traces_.push_back(
61
605
          Trace{trace, std::chrono::steady_clock::now()}); // NO_CHECK_FORMAT(real_time)
62
605
    }
63

            
64
10
    std::string getTraces() override {
65
10
      absl::MutexLock lock(mutex_);
66
10
      std::vector<std::string> string_traces;
67
10
      string_traces.reserve(traces_.size());
68
10
      std::transform(traces_.begin(), traces_.end(), std::back_inserter(string_traces),
69
48
                     [](const Trace& trace) {
70
46
                       return absl::StrCat(trace.trace_, "=",
71
46
                                           trace.time_.time_since_epoch().count());
72
46
                     });
73
10
      return absl::StrJoin(string_traces, ",");
74
10
    }
75

            
76
302
    bool isCancelled() {
77
302
      absl::MutexLock lock(mutex_);
78
302
      return cancelled_;
79
302
    }
80

            
81
    absl::Mutex mutex_;
82
    const std::string dns_name_;
83
    const DnsLookupFamily dns_lookup_family_;
84
    ResolveCb callback_;
85
    bool cancelled_{false};
86
    std::vector<Trace> traces_;
87
  };
88

            
89
  struct PendingQueryInfo {
90
    std::unique_ptr<PendingQuery> pending_query_;
91
    // Empty means it will retry indefinitely until it succeeds.
92
    absl::optional<uint32_t> num_retries_;
93
  };
94

            
95
  // Parse a getaddrinfo() response and determine the final address list. We could potentially avoid
96
  // adding v4 or v6 addresses if we know they will never be used. Right now the final filtering is
97
  // done below and this code is kept simple.
98
  std::pair<ResolutionStatus, std::list<DnsResponse>>
99
  processResponse(const PendingQuery& query, const addrinfo* addrinfo_result);
100

            
101
  // Background thread which wakes up and does resolutions.
102
  void resolveThreadRoutine();
103

            
104
  // getaddrinfo() doesn't provide TTL so use a hard coded default. This can be made configurable
105
  // later if needed.
106
  static constexpr std::chrono::seconds DEFAULT_TTL = std::chrono::seconds(60);
107

            
108
  envoy::extensions::network::dns_resolver::getaddrinfo::v3::GetAddrInfoDnsResolverConfig config_;
109
  Event::Dispatcher& dispatcher_;
110
  Api::Api& api_;
111
  absl::Mutex mutex_;
112
  std::list<PendingQueryInfo> pending_queries_ ABSL_GUARDED_BY(mutex_);
113
  bool shutting_down_ ABSL_GUARDED_BY(mutex_){};
114
  // The resolvers thread must be initialized last so that the above members are already fully
115
  // initialized.
116
  std::vector<Thread::ThreadPtr> resolver_threads_;
117
};
118

            
119
// getaddrinfo DNS resolver factory
120
class GetAddrInfoDnsResolverFactory : public DnsResolverFactory,
121
                                      public Logger::Loggable<Logger::Id::dns> {
122
public:
123
2589
  std::string name() const override { return {"envoy.network.dns_resolver.getaddrinfo"}; }
124

            
125
201
  ProtobufTypes::MessagePtr createEmptyConfigProto() override {
126
201
    return ProtobufTypes::MessagePtr{new envoy::extensions::network::dns_resolver::getaddrinfo::v3::
127
201
                                         GetAddrInfoDnsResolverConfig()};
128
201
  }
129

            
130
  absl::StatusOr<DnsResolverSharedPtr>
131
  createDnsResolver(Event::Dispatcher& dispatcher, Api::Api& api,
132
                    const envoy::config::core::v3::TypedExtensionConfig& typed_getaddrinfo_config)
133
192
      const override {
134
192
    envoy::extensions::network::dns_resolver::getaddrinfo::v3::GetAddrInfoDnsResolverConfig config;
135
192
    RETURN_IF_NOT_OK(Envoy::MessageUtil::unpackTo(typed_getaddrinfo_config.typed_config(), config));
136
192
    return std::make_shared<GetAddrInfoDnsResolver>(config, dispatcher, api);
137
192
  }
138
};
139

            
140
} // namespace Network
141
} // namespace Envoy