00358736a945e4a9bd016f101a790d4e3650a1e0
[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
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/dns/dns_resolver_selection.h"
36 #include "src/core/ext/filters/client_channel/resolver_registry.h"
37 #include "src/core/ext/filters/client_channel/server_address.h"
38 #include "src/core/ext/filters/client_channel/service_config.h"
39 #include "src/core/lib/backoff/backoff.h"
40 #include "src/core/lib/channel/channel_args.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_error** error) {
232   grpc_json* choices_json = grpc_json_parse_string(service_config_choice_json);
233   if (choices_json == nullptr) {
234     *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
235         "Service Config JSON Parsing, error: could not parse");
236     return nullptr;
237   }
238   if (choices_json->type != GRPC_JSON_ARRAY) {
239     *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
240         "Service Config Choices, error: should be of type array");
241     return nullptr;
242   }
243   char* service_config = nullptr;
244   InlinedVector<grpc_error*, 4> error_list;
245   bool found_choice = false;  // have we found a choice?
246   for (grpc_json* choice = choices_json->child; choice != nullptr;
247        choice = choice->next) {
248     if (choice->type != GRPC_JSON_OBJECT) {
249       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
250           "Service Config Choice, error: should be of type object"));
251       continue;
252     }
253     grpc_json* service_config_json = nullptr;
254     bool selected = true;  // has this choice been rejected?
255     for (grpc_json* field = choice->child; field != nullptr;
256          field = field->next) {
257       // Check client language, if specified.
258       if (strcmp(field->key, "clientLanguage") == 0) {
259         if (field->type != GRPC_JSON_ARRAY) {
260           error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
261               "field:clientLanguage error:should be of type array"));
262         } else if (!ValueInJsonArray(field, "c++")) {
263           selected = false;
264         }
265       }
266       // Check client hostname, if specified.
267       if (strcmp(field->key, "clientHostname") == 0) {
268         if (field->type != GRPC_JSON_ARRAY) {
269           error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
270               "field:clientHostname error:should be of type array"));
271           continue;
272         }
273         char* hostname = grpc_gethostname();
274         if (hostname == nullptr || !ValueInJsonArray(field, hostname)) {
275           selected = false;
276         }
277       }
278       // Check percentage, if specified.
279       if (strcmp(field->key, "percentage") == 0) {
280         if (field->type != GRPC_JSON_NUMBER) {
281           error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
282               "field:percentage error:should be of type number"));
283           continue;
284         }
285         int random_pct = rand() % 100;
286         int percentage;
287         if (sscanf(field->value, "%d", &percentage) != 1) {
288           error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
289               "field:percentage error:should be of type integer"));
290           continue;
291         }
292         if (random_pct > percentage || percentage == 0) {
293           selected = false;
294         }
295       }
296       // Save service config.
297       if (strcmp(field->key, "serviceConfig") == 0) {
298         if (field->type == GRPC_JSON_OBJECT) {
299           service_config_json = field;
300         } else {
301           error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
302               "field:serviceConfig error:should be of type object"));
303         }
304       }
305     }
306     if (!found_choice && selected && service_config_json != nullptr) {
307       service_config = grpc_json_dump_to_string(service_config_json, 0);
308       found_choice = true;
309     }
310   }
311   grpc_json_destroy(choices_json);
312   if (!error_list.empty()) {
313     gpr_free(service_config);
314     service_config = nullptr;
315     *error = GRPC_ERROR_CREATE_FROM_VECTOR("Service Config Choices Parser",
316                                            &error_list);
317   }
318   return service_config;
319 }
320
321 void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) {
322   AresDnsResolver* r = static_cast<AresDnsResolver*>(arg);
323   GPR_ASSERT(r->resolving_);
324   r->resolving_ = false;
325   gpr_free(r->pending_request_);
326   r->pending_request_ = nullptr;
327   if (r->shutdown_initiated_) {
328     r->Unref(DEBUG_LOCATION, "OnResolvedLocked() shutdown");
329     return;
330   }
331   if (r->addresses_ != nullptr) {
332     Result result;
333     result.addresses = std::move(*r->addresses_);
334     if (r->service_config_json_ != nullptr) {
335       char* service_config_string = ChooseServiceConfig(
336           r->service_config_json_, &result.service_config_error);
337       gpr_free(r->service_config_json_);
338       if (result.service_config_error == GRPC_ERROR_NONE &&
339           service_config_string != nullptr) {
340         GRPC_CARES_TRACE_LOG("resolver:%p selected service config choice: %s",
341                              r, service_config_string);
342         result.service_config = ServiceConfig::Create(
343             service_config_string, &result.service_config_error);
344       }
345       gpr_free(service_config_string);
346     }
347     result.args = grpc_channel_args_copy(r->channel_args_);
348     r->result_handler()->ReturnResult(std::move(result));
349     r->addresses_.reset();
350     // Reset backoff state so that we start from the beginning when the
351     // next request gets triggered.
352     r->backoff_.Reset();
353   } else {
354     GRPC_CARES_TRACE_LOG("resolver:%p dns resolution failed: %s", r,
355                          grpc_error_string(error));
356     r->result_handler()->ReturnError(grpc_error_set_int(
357         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
358             "DNS resolution failed", &error, 1),
359         GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE));
360     // Set retry timer.
361     grpc_millis next_try = r->backoff_.NextAttemptTime();
362     grpc_millis timeout = next_try - ExecCtx::Get()->Now();
363     GRPC_CARES_TRACE_LOG("resolver:%p dns resolution failed (will retry): %s",
364                          r, grpc_error_string(error));
365     GPR_ASSERT(!r->have_next_resolution_timer_);
366     r->have_next_resolution_timer_ = true;
367     // TODO(roth): We currently deal with this ref manually.  Once the
368     // new closure API is done, find a way to track this ref with the timer
369     // callback as part of the type system.
370     r->Ref(DEBUG_LOCATION, "retry-timer").release();
371     if (timeout > 0) {
372       GRPC_CARES_TRACE_LOG("resolver:%p retrying in %" PRId64 " milliseconds",
373                            r, timeout);
374     } else {
375       GRPC_CARES_TRACE_LOG("resolver:%p retrying immediately", r);
376     }
377     grpc_timer_init(&r->next_resolution_timer_, next_try,
378                     &r->on_next_resolution_);
379   }
380   r->Unref(DEBUG_LOCATION, "dns-resolving");
381 }
382
383 void AresDnsResolver::MaybeStartResolvingLocked() {
384   // If there is an existing timer, the time it fires is the earliest time we
385   // can start the next resolution.
386   if (have_next_resolution_timer_) return;
387   if (last_resolution_timestamp_ >= 0) {
388     const grpc_millis earliest_next_resolution =
389         last_resolution_timestamp_ + min_time_between_resolutions_;
390     const grpc_millis ms_until_next_resolution =
391         earliest_next_resolution - grpc_core::ExecCtx::Get()->Now();
392     if (ms_until_next_resolution > 0) {
393       const grpc_millis last_resolution_ago =
394           grpc_core::ExecCtx::Get()->Now() - last_resolution_timestamp_;
395       GRPC_CARES_TRACE_LOG(
396           "resolver:%p In cooldown from last resolution (from %" PRId64
397           " ms ago). Will resolve again in %" PRId64 " ms",
398           this, last_resolution_ago, ms_until_next_resolution);
399       have_next_resolution_timer_ = true;
400       // TODO(roth): We currently deal with this ref manually.  Once the
401       // new closure API is done, find a way to track this ref with the timer
402       // callback as part of the type system.
403       Ref(DEBUG_LOCATION, "next_resolution_timer_cooldown").release();
404       grpc_timer_init(&next_resolution_timer_, ms_until_next_resolution,
405                       &on_next_resolution_);
406       return;
407     }
408   }
409   StartResolvingLocked();
410 }
411
412 void AresDnsResolver::StartResolvingLocked() {
413   // TODO(roth): We currently deal with this ref manually.  Once the
414   // new closure API is done, find a way to track this ref with the timer
415   // callback as part of the type system.
416   Ref(DEBUG_LOCATION, "dns-resolving").release();
417   GPR_ASSERT(!resolving_);
418   resolving_ = true;
419   service_config_json_ = nullptr;
420   pending_request_ = grpc_dns_lookup_ares_locked(
421       dns_server_, name_to_resolve_, kDefaultPort, interested_parties_,
422       &on_resolved_, &addresses_, enable_srv_queries_ /* check_grpclb */,
423       request_service_config_ ? &service_config_json_ : nullptr,
424       query_timeout_ms_, combiner());
425   last_resolution_timestamp_ = grpc_core::ExecCtx::Get()->Now();
426   GRPC_CARES_TRACE_LOG("resolver:%p Started resolving. pending_request_:%p",
427                        this, pending_request_);
428 }
429
430 //
431 // Factory
432 //
433
434 class AresDnsResolverFactory : public ResolverFactory {
435  public:
436   OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
437     return OrphanablePtr<Resolver>(New<AresDnsResolver>(std::move(args)));
438   }
439
440   const char* scheme() const override { return "dns"; }
441 };
442
443 }  // namespace
444
445 }  // namespace grpc_core
446
447 extern grpc_address_resolver_vtable* grpc_resolve_address_impl;
448 static grpc_address_resolver_vtable* default_resolver;
449
450 static grpc_error* blocking_resolve_address_ares(
451     const char* name, const char* default_port,
452     grpc_resolved_addresses** addresses) {
453   return default_resolver->blocking_resolve_address(name, default_port,
454                                                     addresses);
455 }
456
457 static grpc_address_resolver_vtable ares_resolver = {
458     grpc_resolve_address_ares, blocking_resolve_address_ares};
459
460 #ifdef GRPC_UV
461 /* TODO(murgatroid99): Remove this when we want the cares resolver to be the
462  * default when using libuv */
463 static bool should_use_ares(const char* resolver_env) {
464   return resolver_env != nullptr && gpr_stricmp(resolver_env, "ares") == 0;
465 }
466 #else  /* GRPC_UV */
467 static bool should_use_ares(const char* resolver_env) {
468   // TODO(lidiz): Remove the "g_custom_iomgr_enabled" flag once c-ares support
469   // custom IO managers (e.g. gevent).
470   return !g_custom_iomgr_enabled &&
471          (resolver_env == nullptr || strlen(resolver_env) == 0 ||
472           gpr_stricmp(resolver_env, "ares") == 0);
473 }
474 #endif /* GRPC_UV */
475
476 void grpc_resolver_dns_ares_init() {
477   grpc_core::UniquePtr<char> resolver =
478       GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
479   if (should_use_ares(resolver.get())) {
480     gpr_log(GPR_DEBUG, "Using ares dns resolver");
481     address_sorting_init();
482     grpc_error* error = grpc_ares_init();
483     if (error != GRPC_ERROR_NONE) {
484       GRPC_LOG_IF_ERROR("grpc_ares_init() failed", error);
485       return;
486     }
487     if (default_resolver == nullptr) {
488       default_resolver = grpc_resolve_address_impl;
489     }
490     grpc_set_resolver_impl(&ares_resolver);
491     grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
492         grpc_core::UniquePtr<grpc_core::ResolverFactory>(
493             grpc_core::New<grpc_core::AresDnsResolverFactory>()));
494   }
495 }
496
497 void grpc_resolver_dns_ares_shutdown() {
498   grpc_core::UniquePtr<char> resolver =
499       GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
500   if (should_use_ares(resolver.get())) {
501     address_sorting_shutdown();
502     grpc_ares_cleanup();
503   }
504 }
505
506 #else /* GRPC_ARES == 1 */
507
508 void grpc_resolver_dns_ares_init(void) {}
509
510 void grpc_resolver_dns_ares_shutdown(void) {}
511
512 #endif /* GRPC_ARES == 1 */