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>
21 #if GRPC_ARES == 1 && !defined(GRPC_UV)
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_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"
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_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");
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");
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;
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;
262 // Check percentage, if specified.
263 if (strcmp(field->key, "percentage") == 0) {
264 if (field->type != GRPC_JSON_NUMBER) {
265 service_config_json = nullptr;
268 int random_pct = rand() % 100;
270 if (sscanf(field->value, "%d", &percentage) != 1 ||
271 random_pct > percentage || percentage == 0) {
272 service_config_json = nullptr;
276 // Save service config.
277 if (strcmp(field->key, "serviceConfig") == 0) {
278 if (field->type == GRPC_JSON_OBJECT) {
279 service_config_json = field;
283 if (service_config_json != nullptr) {
284 service_config = grpc_json_dump_to_string(service_config_json, 0);
288 grpc_json_destroy(choices_json);
289 return service_config;
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");
302 if (r->addresses_ != nullptr) {
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);
314 gpr_free(service_config_string);
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.
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));
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();
341 GRPC_CARES_TRACE_LOG("resolver:%p retrying in %" PRId64 " milliseconds",
344 GRPC_CARES_TRACE_LOG("resolver:%p retrying immediately", r);
346 grpc_timer_init(&r->next_resolution_timer_, next_try,
347 &r->on_next_resolution_);
349 r->Unref(DEBUG_LOCATION, "dns-resolving");
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_);
378 StartResolvingLocked();
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_);
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_);
403 class AresDnsResolverFactory : public ResolverFactory {
405 OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
406 return OrphanablePtr<Resolver>(New<AresDnsResolver>(std::move(args)));
409 const char* scheme() const override { return "dns"; }
414 } // namespace grpc_core
416 extern grpc_address_resolver_vtable* grpc_resolve_address_impl;
417 static grpc_address_resolver_vtable* default_resolver;
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,
426 static grpc_address_resolver_vtable ares_resolver = {
427 grpc_resolve_address_ares, blocking_resolve_address_ares};
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);
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);
447 if (default_resolver == nullptr) {
448 default_resolver = grpc_resolve_address_impl;
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>()));
455 gpr_free(resolver_env);
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();
464 gpr_free(resolver_env);
467 #else /* GRPC_ARES == 1 && !defined(GRPC_UV) */
469 void grpc_resolver_dns_ares_init(void) {}
471 void grpc_resolver_dns_ares_shutdown(void) {}
473 #endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */