3 * Copyright 2015 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <grpc/support/port_platform.h>
27 #include <grpc/support/alloc.h>
28 #include <grpc/support/string_util.h>
30 #include <address_sorting/address_sorting.h>
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"
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
60 const char kDefaultPort[] = "https";
62 class AresDnsResolver : public Resolver {
64 explicit AresDnsResolver(ResolverArgs args);
66 void StartLocked() override;
68 void RequestReresolutionLocked() override;
70 void ResetBackoffLocked() override;
72 void ShutdownLocked() override;
75 virtual ~AresDnsResolver();
77 void MaybeStartResolvingLocked();
78 void StartResolvingLocked();
80 static void OnNextResolutionLocked(void* arg, grpc_error* error);
81 static void OnResolvedLocked(void* arg, grpc_error* error);
83 /// DNS server to use (if not system default)
85 /// name to resolve (usually the same as target_name)
86 char* name_to_resolve_;
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
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_;
121 AresDnsResolver::AresDnsResolver(ResolverArgs args)
122 : Resolver(args.combiner, std::move(args.result_handler)),
125 .set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS *
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);
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);
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});
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_);
175 void AresDnsResolver::StartLocked() {
176 GRPC_CARES_TRACE_LOG("resolver:%p AresDnsResolver::StartLocked() is called.",
178 MaybeStartResolvingLocked();
181 void AresDnsResolver::RequestReresolutionLocked() {
183 MaybeStartResolvingLocked();
187 void AresDnsResolver::ResetBackoffLocked() {
188 if (have_next_resolution_timer_) {
189 grpc_timer_cancel(&next_resolution_timer_);
194 void AresDnsResolver::ShutdownLocked() {
195 shutdown_initiated_ = true;
196 if (have_next_resolution_timer_) {
197 grpc_timer_cancel(&next_resolution_timer_);
199 if (pending_request_ != nullptr) {
200 grpc_cancel_ares_request_locked(pending_request_);
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_: "
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();
218 r->Unref(DEBUG_LOCATION, "next_resolution_timer");
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) {
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");
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");
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"));
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++")) {
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"));
273 char* hostname = grpc_gethostname();
274 if (hostname == nullptr || !ValueInJsonArray(field, hostname)) {
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"));
285 int random_pct = rand() % 100;
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"));
292 if (random_pct > percentage || percentage == 0) {
296 // Save service config.
297 if (strcmp(field->key, "serviceConfig") == 0) {
298 if (field->type == GRPC_JSON_OBJECT) {
299 service_config_json = field;
301 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
302 "field:serviceConfig error:should be of type object"));
306 if (!found_choice && selected && service_config_json != nullptr) {
307 service_config = grpc_json_dump_to_string(service_config_json, 0);
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",
318 return service_config;
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");
331 if (r->addresses_ != nullptr) {
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);
345 gpr_free(service_config_string);
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.
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));
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();
372 GRPC_CARES_TRACE_LOG("resolver:%p retrying in %" PRId64 " milliseconds",
375 GRPC_CARES_TRACE_LOG("resolver:%p retrying immediately", r);
377 grpc_timer_init(&r->next_resolution_timer_, next_try,
378 &r->on_next_resolution_);
380 r->Unref(DEBUG_LOCATION, "dns-resolving");
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_);
409 StartResolvingLocked();
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_);
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_);
434 class AresDnsResolverFactory : public ResolverFactory {
436 OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
437 return OrphanablePtr<Resolver>(New<AresDnsResolver>(std::move(args)));
440 const char* scheme() const override { return "dns"; }
445 } // namespace grpc_core
447 extern grpc_address_resolver_vtable* grpc_resolve_address_impl;
448 static grpc_address_resolver_vtable* default_resolver;
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,
457 static grpc_address_resolver_vtable ares_resolver = {
458 grpc_resolve_address_ares, blocking_resolve_address_ares};
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;
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);
476 static bool g_use_ares_dns_resolver;
478 void grpc_resolver_dns_ares_init() {
479 grpc_core::UniquePtr<char> resolver =
480 GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
481 if (should_use_ares(resolver.get())) {
482 g_use_ares_dns_resolver = true;
483 gpr_log(GPR_DEBUG, "Using ares dns resolver");
484 address_sorting_init();
485 grpc_error* error = grpc_ares_init();
486 if (error != GRPC_ERROR_NONE) {
487 GRPC_LOG_IF_ERROR("grpc_ares_init() failed", error);
490 if (default_resolver == nullptr) {
491 default_resolver = grpc_resolve_address_impl;
493 grpc_set_resolver_impl(&ares_resolver);
494 grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
495 grpc_core::UniquePtr<grpc_core::ResolverFactory>(
496 grpc_core::New<grpc_core::AresDnsResolverFactory>()));
498 g_use_ares_dns_resolver = false;
502 void grpc_resolver_dns_ares_shutdown() {
503 if (g_use_ares_dns_resolver) {
504 address_sorting_shutdown();
509 #else /* GRPC_ARES == 1 */
511 void grpc_resolver_dns_ares_init(void) {}
513 void grpc_resolver_dns_ares_shutdown(void) {}
515 #endif /* GRPC_ARES == 1 */