13fde4aeccd517f5f295dd3d4e240f7e9bcaebd9
[platform/upstream/grpc.git] / src / core / ext / filters / client_channel / resolver / dns / c_ares / dns_resolver_ares.cc
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 #if GRPC_ARES == 1 && !defined(GRPC_UV)
22
23 #include <limits.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <grpc/support/alloc.h>
28 #include <grpc/support/string_util.h>
29
30 #include <address_sorting/address_sorting.h>
31
32 #include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
33 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
34 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
35 #include "src/core/ext/filters/client_channel/resolver_registry.h"
36 #include "src/core/ext/filters/client_channel/server_address.h"
37 #include "src/core/ext/filters/client_channel/service_config.h"
38 #include "src/core/lib/backoff/backoff.h"
39 #include "src/core/lib/channel/channel_args.h"
40 #include "src/core/lib/gpr/env.h"
41 #include "src/core/lib/gpr/host_port.h"
42 #include "src/core/lib/gpr/string.h"
43 #include "src/core/lib/gprpp/manual_constructor.h"
44 #include "src/core/lib/iomgr/combiner.h"
45 #include "src/core/lib/iomgr/gethostname.h"
46 #include "src/core/lib/iomgr/iomgr_custom.h"
47 #include "src/core/lib/iomgr/resolve_address.h"
48 #include "src/core/lib/iomgr/timer.h"
49 #include "src/core/lib/json/json.h"
50
51 #define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
52 #define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6
53 #define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120
54 #define GRPC_DNS_RECONNECT_JITTER 0.2
55
56 namespace grpc_core {
57
58 namespace {
59
60 const char kDefaultPort[] = "https";
61
62 class AresDnsResolver : public Resolver {
63  public:
64   explicit AresDnsResolver(ResolverArgs args);
65
66   void StartLocked() override;
67
68   void RequestReresolutionLocked() override;
69
70   void ResetBackoffLocked() override;
71
72   void ShutdownLocked() override;
73
74  private:
75   virtual ~AresDnsResolver();
76
77   void MaybeStartResolvingLocked();
78   void StartResolvingLocked();
79
80   static void OnNextResolutionLocked(void* arg, grpc_error* error);
81   static void OnResolvedLocked(void* arg, grpc_error* error);
82
83   /// DNS server to use (if not system default)
84   char* dns_server_;
85   /// name to resolve (usually the same as target_name)
86   char* name_to_resolve_;
87   /// channel args
88   grpc_channel_args* channel_args_;
89   /// whether to request the service config
90   bool request_service_config_;
91   /// pollset_set to drive the name resolution process
92   grpc_pollset_set* interested_parties_;
93   /// closures used by the combiner
94   grpc_closure on_next_resolution_;
95   grpc_closure on_resolved_;
96   /// are we currently resolving?
97   bool resolving_ = false;
98   /// the pending resolving request
99   grpc_ares_request* pending_request_ = nullptr;
100   /// next resolution timer
101   bool have_next_resolution_timer_ = false;
102   grpc_timer next_resolution_timer_;
103   /// min interval between DNS requests
104   grpc_millis min_time_between_resolutions_;
105   /// timestamp of last DNS request
106   grpc_millis last_resolution_timestamp_ = -1;
107   /// retry backoff state
108   BackOff backoff_;
109   /// currently resolving addresses
110   UniquePtr<ServerAddressList> addresses_;
111   /// currently resolving service config
112   char* service_config_json_ = nullptr;
113   // has shutdown been initiated
114   bool shutdown_initiated_ = false;
115   // timeout in milliseconds for active DNS queries
116   int query_timeout_ms_;
117   // whether or not to enable SRV DNS queries
118   bool enable_srv_queries_;
119 };
120
121 AresDnsResolver::AresDnsResolver(ResolverArgs args)
122     : Resolver(args.combiner, std::move(args.result_handler)),
123       backoff_(
124           BackOff::Options()
125               .set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS *
126                                    1000)
127               .set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER)
128               .set_jitter(GRPC_DNS_RECONNECT_JITTER)
129               .set_max_backoff(GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)) {
130   // Get name to resolve from URI path.
131   const char* path = args.uri->path;
132   if (path[0] == '/') ++path;
133   name_to_resolve_ = gpr_strdup(path);
134   // Get DNS server from URI authority.
135   dns_server_ = nullptr;
136   if (0 != strcmp(args.uri->authority, "")) {
137     dns_server_ = gpr_strdup(args.uri->authority);
138   }
139   channel_args_ = grpc_channel_args_copy(args.args);
140   // Disable service config option
141   const grpc_arg* arg = grpc_channel_args_find(
142       channel_args_, GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION);
143   request_service_config_ = !grpc_channel_arg_get_bool(arg, true);
144   // Min time b/t resolutions option
145   arg = grpc_channel_args_find(channel_args_,
146                                GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS);
147   min_time_between_resolutions_ =
148       grpc_channel_arg_get_integer(arg, {1000, 0, INT_MAX});
149   // Enable SRV queries option
150   arg = grpc_channel_args_find(channel_args_, GRPC_ARG_DNS_ENABLE_SRV_QUERIES);
151   enable_srv_queries_ = grpc_channel_arg_get_bool(arg, false);
152   interested_parties_ = grpc_pollset_set_create();
153   if (args.pollset_set != nullptr) {
154     grpc_pollset_set_add_pollset_set(interested_parties_, args.pollset_set);
155   }
156   GRPC_CLOSURE_INIT(&on_next_resolution_, OnNextResolutionLocked, this,
157                     grpc_combiner_scheduler(combiner()));
158   GRPC_CLOSURE_INIT(&on_resolved_, OnResolvedLocked, this,
159                     grpc_combiner_scheduler(combiner()));
160   const grpc_arg* query_timeout_ms_arg =
161       grpc_channel_args_find(channel_args_, GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS);
162   query_timeout_ms_ = grpc_channel_arg_get_integer(
163       query_timeout_ms_arg,
164       {GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS, 0, INT_MAX});
165 }
166
167 AresDnsResolver::~AresDnsResolver() {
168   GRPC_CARES_TRACE_LOG("resolver:%p destroying AresDnsResolver", this);
169   grpc_pollset_set_destroy(interested_parties_);
170   gpr_free(dns_server_);
171   gpr_free(name_to_resolve_);
172   grpc_channel_args_destroy(channel_args_);
173 }
174
175 void AresDnsResolver::StartLocked() {
176   GRPC_CARES_TRACE_LOG("resolver:%p AresDnsResolver::StartLocked() is called.",
177                        this);
178   MaybeStartResolvingLocked();
179 }
180
181 void AresDnsResolver::RequestReresolutionLocked() {
182   if (!resolving_) {
183     MaybeStartResolvingLocked();
184   }
185 }
186
187 void AresDnsResolver::ResetBackoffLocked() {
188   if (have_next_resolution_timer_) {
189     grpc_timer_cancel(&next_resolution_timer_);
190   }
191   backoff_.Reset();
192 }
193
194 void AresDnsResolver::ShutdownLocked() {
195   shutdown_initiated_ = true;
196   if (have_next_resolution_timer_) {
197     grpc_timer_cancel(&next_resolution_timer_);
198   }
199   if (pending_request_ != nullptr) {
200     grpc_cancel_ares_request_locked(pending_request_);
201   }
202 }
203
204 void AresDnsResolver::OnNextResolutionLocked(void* arg, grpc_error* error) {
205   AresDnsResolver* r = static_cast<AresDnsResolver*>(arg);
206   GRPC_CARES_TRACE_LOG(
207       "resolver:%p re-resolution timer fired. error: %s. shutdown_initiated_: "
208       "%d",
209       r, grpc_error_string(error), r->shutdown_initiated_);
210   r->have_next_resolution_timer_ = false;
211   if (error == GRPC_ERROR_NONE && !r->shutdown_initiated_) {
212     if (!r->resolving_) {
213       GRPC_CARES_TRACE_LOG(
214           "resolver:%p start resolving due to re-resolution timer", r);
215       r->StartResolvingLocked();
216     }
217   }
218   r->Unref(DEBUG_LOCATION, "next_resolution_timer");
219 }
220
221 bool ValueInJsonArray(grpc_json* array, const char* value) {
222   for (grpc_json* entry = array->child; entry != nullptr; entry = entry->next) {
223     if (entry->type == GRPC_JSON_STRING && strcmp(entry->value, value) == 0) {
224       return true;
225     }
226   }
227   return false;
228 }
229
230 char* ChooseServiceConfig(char* service_config_choice_json) {
231   grpc_json* choices_json = grpc_json_parse_string(service_config_choice_json);
232   if (choices_json == nullptr || choices_json->type != GRPC_JSON_ARRAY) {
233     gpr_log(GPR_ERROR, "cannot parse service config JSON string");
234     return nullptr;
235   }
236   char* service_config = nullptr;
237   for (grpc_json* choice = choices_json->child; choice != nullptr;
238        choice = choice->next) {
239     if (choice->type != GRPC_JSON_OBJECT) {
240       gpr_log(GPR_ERROR, "cannot parse service config JSON string");
241       break;
242     }
243     grpc_json* service_config_json = nullptr;
244     for (grpc_json* field = choice->child; field != nullptr;
245          field = field->next) {
246       // Check client language, if specified.
247       if (strcmp(field->key, "clientLanguage") == 0) {
248         if (field->type != GRPC_JSON_ARRAY || !ValueInJsonArray(field, "c++")) {
249           service_config_json = nullptr;
250           break;
251         }
252       }
253       // Check client hostname, if specified.
254       if (strcmp(field->key, "clientHostname") == 0) {
255         char* hostname = grpc_gethostname();
256         if (hostname == nullptr || field->type != GRPC_JSON_ARRAY ||
257             !ValueInJsonArray(field, hostname)) {
258           service_config_json = nullptr;
259           break;
260         }
261       }
262       // Check percentage, if specified.
263       if (strcmp(field->key, "percentage") == 0) {
264         if (field->type != GRPC_JSON_NUMBER) {
265           service_config_json = nullptr;
266           break;
267         }
268         int random_pct = rand() % 100;
269         int percentage;
270         if (sscanf(field->value, "%d", &percentage) != 1 ||
271             random_pct > percentage || percentage == 0) {
272           service_config_json = nullptr;
273           break;
274         }
275       }
276       // Save service config.
277       if (strcmp(field->key, "serviceConfig") == 0) {
278         if (field->type == GRPC_JSON_OBJECT) {
279           service_config_json = field;
280         }
281       }
282     }
283     if (service_config_json != nullptr) {
284       service_config = grpc_json_dump_to_string(service_config_json, 0);
285       break;
286     }
287   }
288   grpc_json_destroy(choices_json);
289   return service_config;
290 }
291
292 void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) {
293   AresDnsResolver* r = static_cast<AresDnsResolver*>(arg);
294   GPR_ASSERT(r->resolving_);
295   r->resolving_ = false;
296   gpr_free(r->pending_request_);
297   r->pending_request_ = nullptr;
298   if (r->shutdown_initiated_) {
299     r->Unref(DEBUG_LOCATION, "OnResolvedLocked() shutdown");
300     return;
301   }
302   if (r->addresses_ != nullptr) {
303     Result result;
304     result.addresses = std::move(*r->addresses_);
305     if (r->service_config_json_ != nullptr) {
306       char* service_config_string =
307           ChooseServiceConfig(r->service_config_json_);
308       gpr_free(r->service_config_json_);
309       if (service_config_string != nullptr) {
310         GRPC_CARES_TRACE_LOG("resolver:%p selected service config choice: %s",
311                              r, service_config_string);
312         result.service_config = ServiceConfig::Create(service_config_string);
313       }
314       gpr_free(service_config_string);
315     }
316     result.args = grpc_channel_args_copy(r->channel_args_);
317     r->result_handler()->ReturnResult(std::move(result));
318     r->addresses_.reset();
319     // Reset backoff state so that we start from the beginning when the
320     // next request gets triggered.
321     r->backoff_.Reset();
322   } else {
323     GRPC_CARES_TRACE_LOG("resolver:%p dns resolution failed: %s", r,
324                          grpc_error_string(error));
325     r->result_handler()->ReturnError(grpc_error_set_int(
326         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
327             "DNS resolution failed", &error, 1),
328         GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE));
329     // Set retry timer.
330     grpc_millis next_try = r->backoff_.NextAttemptTime();
331     grpc_millis timeout = next_try - ExecCtx::Get()->Now();
332     GRPC_CARES_TRACE_LOG("resolver:%p dns resolution failed (will retry): %s",
333                          r, grpc_error_string(error));
334     GPR_ASSERT(!r->have_next_resolution_timer_);
335     r->have_next_resolution_timer_ = true;
336     // TODO(roth): We currently deal with this ref manually.  Once the
337     // new closure API is done, find a way to track this ref with the timer
338     // callback as part of the type system.
339     r->Ref(DEBUG_LOCATION, "retry-timer").release();
340     if (timeout > 0) {
341       GRPC_CARES_TRACE_LOG("resolver:%p retrying in %" PRId64 " milliseconds",
342                            r, timeout);
343     } else {
344       GRPC_CARES_TRACE_LOG("resolver:%p retrying immediately", r);
345     }
346     grpc_timer_init(&r->next_resolution_timer_, next_try,
347                     &r->on_next_resolution_);
348   }
349   r->Unref(DEBUG_LOCATION, "dns-resolving");
350 }
351
352 void AresDnsResolver::MaybeStartResolvingLocked() {
353   // If there is an existing timer, the time it fires is the earliest time we
354   // can start the next resolution.
355   if (have_next_resolution_timer_) return;
356   if (last_resolution_timestamp_ >= 0) {
357     const grpc_millis earliest_next_resolution =
358         last_resolution_timestamp_ + min_time_between_resolutions_;
359     const grpc_millis ms_until_next_resolution =
360         earliest_next_resolution - grpc_core::ExecCtx::Get()->Now();
361     if (ms_until_next_resolution > 0) {
362       const grpc_millis last_resolution_ago =
363           grpc_core::ExecCtx::Get()->Now() - last_resolution_timestamp_;
364       GRPC_CARES_TRACE_LOG(
365           "resolver:%p In cooldown from last resolution (from %" PRId64
366           " ms ago). Will resolve again in %" PRId64 " ms",
367           this, last_resolution_ago, ms_until_next_resolution);
368       have_next_resolution_timer_ = true;
369       // TODO(roth): We currently deal with this ref manually.  Once the
370       // new closure API is done, find a way to track this ref with the timer
371       // callback as part of the type system.
372       Ref(DEBUG_LOCATION, "next_resolution_timer_cooldown").release();
373       grpc_timer_init(&next_resolution_timer_, ms_until_next_resolution,
374                       &on_next_resolution_);
375       return;
376     }
377   }
378   StartResolvingLocked();
379 }
380
381 void AresDnsResolver::StartResolvingLocked() {
382   // TODO(roth): We currently deal with this ref manually.  Once the
383   // new closure API is done, find a way to track this ref with the timer
384   // callback as part of the type system.
385   Ref(DEBUG_LOCATION, "dns-resolving").release();
386   GPR_ASSERT(!resolving_);
387   resolving_ = true;
388   service_config_json_ = nullptr;
389   pending_request_ = grpc_dns_lookup_ares_locked(
390       dns_server_, name_to_resolve_, kDefaultPort, interested_parties_,
391       &on_resolved_, &addresses_, enable_srv_queries_ /* check_grpclb */,
392       request_service_config_ ? &service_config_json_ : nullptr,
393       query_timeout_ms_, combiner());
394   last_resolution_timestamp_ = grpc_core::ExecCtx::Get()->Now();
395   GRPC_CARES_TRACE_LOG("resolver:%p Started resolving. pending_request_:%p",
396                        this, pending_request_);
397 }
398
399 //
400 // Factory
401 //
402
403 class AresDnsResolverFactory : public ResolverFactory {
404  public:
405   OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
406     return OrphanablePtr<Resolver>(New<AresDnsResolver>(std::move(args)));
407   }
408
409   const char* scheme() const override { return "dns"; }
410 };
411
412 }  // namespace
413
414 }  // namespace grpc_core
415
416 extern grpc_address_resolver_vtable* grpc_resolve_address_impl;
417 static grpc_address_resolver_vtable* default_resolver;
418
419 static grpc_error* blocking_resolve_address_ares(
420     const char* name, const char* default_port,
421     grpc_resolved_addresses** addresses) {
422   return default_resolver->blocking_resolve_address(name, default_port,
423                                                     addresses);
424 }
425
426 static grpc_address_resolver_vtable ares_resolver = {
427     grpc_resolve_address_ares, blocking_resolve_address_ares};
428
429 static bool should_use_ares(const char* resolver_env) {
430   // TODO(lidiz): Remove the "g_custom_iomgr_enabled" flag once c-ares support
431   // custom IO managers (e.g. gevent).
432   return !g_custom_iomgr_enabled &&
433          (resolver_env == nullptr || strlen(resolver_env) == 0 ||
434           gpr_stricmp(resolver_env, "ares") == 0);
435 }
436
437 void grpc_resolver_dns_ares_init() {
438   char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER");
439   if (should_use_ares(resolver_env)) {
440     gpr_log(GPR_DEBUG, "Using ares dns resolver");
441     address_sorting_init();
442     grpc_error* error = grpc_ares_init();
443     if (error != GRPC_ERROR_NONE) {
444       GRPC_LOG_IF_ERROR("grpc_ares_init() failed", error);
445       return;
446     }
447     if (default_resolver == nullptr) {
448       default_resolver = grpc_resolve_address_impl;
449     }
450     grpc_set_resolver_impl(&ares_resolver);
451     grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
452         grpc_core::UniquePtr<grpc_core::ResolverFactory>(
453             grpc_core::New<grpc_core::AresDnsResolverFactory>()));
454   }
455   gpr_free(resolver_env);
456 }
457
458 void grpc_resolver_dns_ares_shutdown() {
459   char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER");
460   if (should_use_ares(resolver_env)) {
461     address_sorting_shutdown();
462     grpc_ares_cleanup();
463   }
464   gpr_free(resolver_env);
465 }
466
467 #else /* GRPC_ARES == 1 && !defined(GRPC_UV) */
468
469 void grpc_resolver_dns_ares_init(void) {}
470
471 void grpc_resolver_dns_ares_shutdown(void) {}
472
473 #endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */