• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #include <grpc/support/port_platform.h>
20 
21 #include <inttypes.h>
22 #include <climits>
23 #include <cstring>
24 
25 #include <grpc/support/alloc.h>
26 #include <grpc/support/string_util.h>
27 #include <grpc/support/time.h>
28 
29 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
30 #include "src/core/ext/filters/client_channel/resolver_registry.h"
31 #include "src/core/lib/backoff/backoff.h"
32 #include "src/core/lib/channel/channel_args.h"
33 #include "src/core/lib/gpr/env.h"
34 #include "src/core/lib/gpr/host_port.h"
35 #include "src/core/lib/gpr/string.h"
36 #include "src/core/lib/gprpp/manual_constructor.h"
37 #include "src/core/lib/iomgr/combiner.h"
38 #include "src/core/lib/iomgr/resolve_address.h"
39 #include "src/core/lib/iomgr/timer.h"
40 
41 #define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
42 #define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6
43 #define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120
44 #define GRPC_DNS_RECONNECT_JITTER 0.2
45 
46 namespace grpc_core {
47 
48 namespace {
49 
50 const char kDefaultPort[] = "https";
51 
52 class NativeDnsResolver : public Resolver {
53  public:
54   explicit NativeDnsResolver(const ResolverArgs& args);
55 
56   void NextLocked(grpc_channel_args** result,
57                   grpc_closure* on_complete) override;
58 
59   void RequestReresolutionLocked() override;
60 
61   void ResetBackoffLocked() override;
62 
63   void ShutdownLocked() override;
64 
65  private:
66   virtual ~NativeDnsResolver();
67 
68   void MaybeStartResolvingLocked();
69   void StartResolvingLocked();
70   void MaybeFinishNextLocked();
71 
72   static void OnNextResolutionLocked(void* arg, grpc_error* error);
73   static void OnResolvedLocked(void* arg, grpc_error* error);
74 
75   /// name to resolve
76   char* name_to_resolve_ = nullptr;
77   /// channel args
78   grpc_channel_args* channel_args_ = nullptr;
79   /// pollset_set to drive the name resolution process
80   grpc_pollset_set* interested_parties_ = nullptr;
81   /// are we currently resolving?
82   bool resolving_ = false;
83   grpc_closure on_resolved_;
84   /// which version of the result have we published?
85   int published_version_ = 0;
86   /// which version of the result is current?
87   int resolved_version_ = 0;
88   /// pending next completion, or nullptr
89   grpc_closure* next_completion_ = nullptr;
90   /// target result address for next completion
91   grpc_channel_args** target_result_ = nullptr;
92   /// current (fully resolved) result
93   grpc_channel_args* resolved_result_ = nullptr;
94   /// next resolution timer
95   bool have_next_resolution_timer_ = false;
96   grpc_timer next_resolution_timer_;
97   grpc_closure on_next_resolution_;
98   /// min time between DNS requests
99   grpc_millis min_time_between_resolutions_;
100   /// timestamp of last DNS request
101   grpc_millis last_resolution_timestamp_ = -1;
102   /// retry backoff state
103   BackOff backoff_;
104   /// currently resolving addresses
105   grpc_resolved_addresses* addresses_ = nullptr;
106 };
107 
NativeDnsResolver(const ResolverArgs & args)108 NativeDnsResolver::NativeDnsResolver(const ResolverArgs& args)
109     : Resolver(args.combiner),
110       backoff_(
111           BackOff::Options()
112               .set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS *
113                                    1000)
114               .set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER)
115               .set_jitter(GRPC_DNS_RECONNECT_JITTER)
116               .set_max_backoff(GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)) {
117   char* path = args.uri->path;
118   if (path[0] == '/') ++path;
119   name_to_resolve_ = gpr_strdup(path);
120   channel_args_ = grpc_channel_args_copy(args.args);
121   const grpc_arg* arg = grpc_channel_args_find(
122       args.args, GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS);
123   min_time_between_resolutions_ =
124       grpc_channel_arg_get_integer(arg, {1000, 0, INT_MAX});
125   interested_parties_ = grpc_pollset_set_create();
126   if (args.pollset_set != nullptr) {
127     grpc_pollset_set_add_pollset_set(interested_parties_, args.pollset_set);
128   }
129   GRPC_CLOSURE_INIT(&on_next_resolution_,
130                     NativeDnsResolver::OnNextResolutionLocked, this,
131                     grpc_combiner_scheduler(args.combiner));
132   GRPC_CLOSURE_INIT(&on_resolved_, NativeDnsResolver::OnResolvedLocked, this,
133                     grpc_combiner_scheduler(args.combiner));
134 }
135 
~NativeDnsResolver()136 NativeDnsResolver::~NativeDnsResolver() {
137   if (resolved_result_ != nullptr) {
138     grpc_channel_args_destroy(resolved_result_);
139   }
140   grpc_pollset_set_destroy(interested_parties_);
141   gpr_free(name_to_resolve_);
142   grpc_channel_args_destroy(channel_args_);
143 }
144 
NextLocked(grpc_channel_args ** result,grpc_closure * on_complete)145 void NativeDnsResolver::NextLocked(grpc_channel_args** result,
146                                    grpc_closure* on_complete) {
147   GPR_ASSERT(next_completion_ == nullptr);
148   next_completion_ = on_complete;
149   target_result_ = result;
150   if (resolved_version_ == 0 && !resolving_) {
151     MaybeStartResolvingLocked();
152   } else {
153     MaybeFinishNextLocked();
154   }
155 }
156 
RequestReresolutionLocked()157 void NativeDnsResolver::RequestReresolutionLocked() {
158   if (!resolving_) {
159     MaybeStartResolvingLocked();
160   }
161 }
162 
ResetBackoffLocked()163 void NativeDnsResolver::ResetBackoffLocked() {
164   if (have_next_resolution_timer_) {
165     grpc_timer_cancel(&next_resolution_timer_);
166   }
167   backoff_.Reset();
168 }
169 
ShutdownLocked()170 void NativeDnsResolver::ShutdownLocked() {
171   if (have_next_resolution_timer_) {
172     grpc_timer_cancel(&next_resolution_timer_);
173   }
174   if (next_completion_ != nullptr) {
175     *target_result_ = nullptr;
176     GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
177                                              "Resolver Shutdown"));
178     next_completion_ = nullptr;
179   }
180 }
181 
OnNextResolutionLocked(void * arg,grpc_error * error)182 void NativeDnsResolver::OnNextResolutionLocked(void* arg, grpc_error* error) {
183   NativeDnsResolver* r = static_cast<NativeDnsResolver*>(arg);
184   r->have_next_resolution_timer_ = false;
185   if (error == GRPC_ERROR_NONE && !r->resolving_) {
186     r->StartResolvingLocked();
187   }
188   r->Unref(DEBUG_LOCATION, "retry-timer");
189 }
190 
OnResolvedLocked(void * arg,grpc_error * error)191 void NativeDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) {
192   NativeDnsResolver* r = static_cast<NativeDnsResolver*>(arg);
193   grpc_channel_args* result = nullptr;
194   GPR_ASSERT(r->resolving_);
195   r->resolving_ = false;
196   GRPC_ERROR_REF(error);
197   error =
198       grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS,
199                          grpc_slice_from_copied_string(r->name_to_resolve_));
200   if (r->addresses_ != nullptr) {
201     grpc_lb_addresses* addresses = grpc_lb_addresses_create(
202         r->addresses_->naddrs, nullptr /* user_data_vtable */);
203     for (size_t i = 0; i < r->addresses_->naddrs; ++i) {
204       grpc_lb_addresses_set_address(
205           addresses, i, &r->addresses_->addrs[i].addr,
206           r->addresses_->addrs[i].len, false /* is_balancer */,
207           nullptr /* balancer_name */, nullptr /* user_data */);
208     }
209     grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(addresses);
210     result = grpc_channel_args_copy_and_add(r->channel_args_, &new_arg, 1);
211     grpc_resolved_addresses_destroy(r->addresses_);
212     grpc_lb_addresses_destroy(addresses);
213     // Reset backoff state so that we start from the beginning when the
214     // next request gets triggered.
215     r->backoff_.Reset();
216   } else {
217     grpc_millis next_try = r->backoff_.NextAttemptTime();
218     grpc_millis timeout = next_try - ExecCtx::Get()->Now();
219     gpr_log(GPR_INFO, "dns resolution failed (will retry): %s",
220             grpc_error_string(error));
221     GPR_ASSERT(!r->have_next_resolution_timer_);
222     r->have_next_resolution_timer_ = true;
223     // TODO(roth): We currently deal with this ref manually.  Once the
224     // new closure API is done, find a way to track this ref with the timer
225     // callback as part of the type system.
226     RefCountedPtr<Resolver> self =
227         r->Ref(DEBUG_LOCATION, "next_resolution_timer");
228     self.release();
229     if (timeout > 0) {
230       gpr_log(GPR_DEBUG, "retrying in %" PRId64 " milliseconds", timeout);
231     } else {
232       gpr_log(GPR_DEBUG, "retrying immediately");
233     }
234     grpc_timer_init(&r->next_resolution_timer_, next_try,
235                     &r->on_next_resolution_);
236   }
237   if (r->resolved_result_ != nullptr) {
238     grpc_channel_args_destroy(r->resolved_result_);
239   }
240   r->resolved_result_ = result;
241   ++r->resolved_version_;
242   r->MaybeFinishNextLocked();
243   GRPC_ERROR_UNREF(error);
244   r->Unref(DEBUG_LOCATION, "dns-resolving");
245 }
246 
MaybeStartResolvingLocked()247 void NativeDnsResolver::MaybeStartResolvingLocked() {
248   // If there is an existing timer, the time it fires is the earliest time we
249   // can start the next resolution.
250   if (have_next_resolution_timer_) return;
251   if (last_resolution_timestamp_ >= 0) {
252     const grpc_millis earliest_next_resolution =
253         last_resolution_timestamp_ + min_time_between_resolutions_;
254     const grpc_millis ms_until_next_resolution =
255         earliest_next_resolution - grpc_core::ExecCtx::Get()->Now();
256     if (ms_until_next_resolution > 0) {
257       const grpc_millis last_resolution_ago =
258           grpc_core::ExecCtx::Get()->Now() - last_resolution_timestamp_;
259       gpr_log(GPR_DEBUG,
260               "In cooldown from last resolution (from %" PRId64
261               " ms ago). Will resolve again in %" PRId64 " ms",
262               last_resolution_ago, ms_until_next_resolution);
263       have_next_resolution_timer_ = true;
264       // TODO(roth): We currently deal with this ref manually.  Once the
265       // new closure API is done, find a way to track this ref with the timer
266       // callback as part of the type system.
267       RefCountedPtr<Resolver> self =
268           Ref(DEBUG_LOCATION, "next_resolution_timer_cooldown");
269       self.release();
270       grpc_timer_init(&next_resolution_timer_, ms_until_next_resolution,
271                       &on_next_resolution_);
272       return;
273     }
274   }
275   StartResolvingLocked();
276 }
277 
StartResolvingLocked()278 void NativeDnsResolver::StartResolvingLocked() {
279   gpr_log(GPR_DEBUG, "Start resolving.");
280   // TODO(roth): We currently deal with this ref manually.  Once the
281   // new closure API is done, find a way to track this ref with the timer
282   // callback as part of the type system.
283   RefCountedPtr<Resolver> self = Ref(DEBUG_LOCATION, "dns-resolving");
284   self.release();
285   GPR_ASSERT(!resolving_);
286   resolving_ = true;
287   addresses_ = nullptr;
288   grpc_resolve_address(name_to_resolve_, kDefaultPort, interested_parties_,
289                        &on_resolved_, &addresses_);
290   last_resolution_timestamp_ = grpc_core::ExecCtx::Get()->Now();
291 }
292 
MaybeFinishNextLocked()293 void NativeDnsResolver::MaybeFinishNextLocked() {
294   if (next_completion_ != nullptr && resolved_version_ != published_version_) {
295     *target_result_ = resolved_result_ == nullptr
296                           ? nullptr
297                           : grpc_channel_args_copy(resolved_result_);
298     GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_NONE);
299     next_completion_ = nullptr;
300     published_version_ = resolved_version_;
301   }
302 }
303 
304 //
305 // Factory
306 //
307 
308 class NativeDnsResolverFactory : public ResolverFactory {
309  public:
CreateResolver(const ResolverArgs & args) const310   OrphanablePtr<Resolver> CreateResolver(
311       const ResolverArgs& args) const override {
312     if (GPR_UNLIKELY(0 != strcmp(args.uri->authority, ""))) {
313       gpr_log(GPR_ERROR, "authority based dns uri's not supported");
314       return OrphanablePtr<Resolver>(nullptr);
315     }
316     return OrphanablePtr<Resolver>(New<NativeDnsResolver>(args));
317   }
318 
scheme() const319   const char* scheme() const override { return "dns"; }
320 };
321 
322 }  // namespace
323 
324 }  // namespace grpc_core
325 
grpc_resolver_dns_native_init()326 void grpc_resolver_dns_native_init() {
327   char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER");
328   if (resolver_env != nullptr && gpr_stricmp(resolver_env, "native") == 0) {
329     gpr_log(GPR_DEBUG, "Using native dns resolver");
330     grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
331         grpc_core::UniquePtr<grpc_core::ResolverFactory>(
332             grpc_core::New<grpc_core::NativeDnsResolverFactory>()));
333   } else {
334     grpc_core::ResolverRegistry::Builder::InitRegistry();
335     grpc_core::ResolverFactory* existing_factory =
336         grpc_core::ResolverRegistry::LookupResolverFactory("dns");
337     if (existing_factory == nullptr) {
338       gpr_log(GPR_DEBUG, "Using native dns resolver");
339       grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
340           grpc_core::UniquePtr<grpc_core::ResolverFactory>(
341               grpc_core::New<grpc_core::NativeDnsResolverFactory>()));
342     }
343   }
344   gpr_free(resolver_env);
345 }
346 
grpc_resolver_dns_native_shutdown()347 void grpc_resolver_dns_native_shutdown() {}
348