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