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 "absl/strings/str_cat.h"
26
27 #include <grpc/support/alloc.h>
28 #include <grpc/support/string_util.h>
29 #include <grpc/support/time.h>
30
31 #include "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
32 #include "src/core/ext/filters/client_channel/resolver_registry.h"
33 #include "src/core/ext/filters/client_channel/server_address.h"
34 #include "src/core/lib/backoff/backoff.h"
35 #include "src/core/lib/channel/channel_args.h"
36 #include "src/core/lib/gpr/string.h"
37 #include "src/core/lib/gprpp/manual_constructor.h"
38 #include "src/core/lib/iomgr/resolve_address.h"
39 #include "src/core/lib/iomgr/timer.h"
40 #include "src/core/lib/iomgr/work_serializer.h"
41
42 #define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
43 #define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6
44 #define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120
45 #define GRPC_DNS_RECONNECT_JITTER 0.2
46
47 namespace grpc_core {
48
49 namespace {
50
51 const char kDefaultPort[] = "https";
52
53 class NativeDnsResolver : public Resolver {
54 public:
55 explicit NativeDnsResolver(ResolverArgs args);
56
57 void StartLocked() override;
58
59 void RequestReresolutionLocked() override;
60
61 void ResetBackoffLocked() override;
62
63 void ShutdownLocked() override;
64
65 private:
66 ~NativeDnsResolver() override;
67
68 void MaybeStartResolvingLocked();
69 void StartResolvingLocked();
70
71 static void OnNextResolution(void* arg, grpc_error* error);
72 void OnNextResolutionLocked(grpc_error* error);
73 static void OnResolved(void* arg, grpc_error* error);
74 void OnResolvedLocked(grpc_error* error);
75
76 /// name to resolve
77 std::string name_to_resolve_;
78 /// channel args
79 grpc_channel_args* channel_args_ = nullptr;
80 std::shared_ptr<WorkSerializer> work_serializer_;
81 std::unique_ptr<ResultHandler> result_handler_;
82 /// pollset_set to drive the name resolution process
83 grpc_pollset_set* interested_parties_ = nullptr;
84 /// are we shutting down?
85 bool shutdown_ = false;
86 /// are we currently resolving?
87 bool resolving_ = false;
88 grpc_closure on_resolved_;
89 /// next resolution timer
90 bool have_next_resolution_timer_ = false;
91 grpc_timer next_resolution_timer_;
92 grpc_closure on_next_resolution_;
93 /// min time between DNS requests
94 grpc_millis min_time_between_resolutions_;
95 /// timestamp of last DNS request
96 grpc_millis last_resolution_timestamp_ = -1;
97 /// retry backoff state
98 BackOff backoff_;
99 /// currently resolving addresses
100 grpc_resolved_addresses* addresses_ = nullptr;
101 };
102
NativeDnsResolver(ResolverArgs args)103 NativeDnsResolver::NativeDnsResolver(ResolverArgs args)
104 : name_to_resolve_(absl::StripPrefix(args.uri.path(), "/")),
105 channel_args_(grpc_channel_args_copy(args.args)),
106 work_serializer_(std::move(args.work_serializer)),
107 result_handler_(std::move(args.result_handler)),
108 interested_parties_(grpc_pollset_set_create()),
109 min_time_between_resolutions_(grpc_channel_args_find_integer(
110 channel_args_, GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS,
111 {1000 * 30, 0, INT_MAX})),
112 backoff_(
113 BackOff::Options()
114 .set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS *
115 1000)
116 .set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER)
117 .set_jitter(GRPC_DNS_RECONNECT_JITTER)
118 .set_max_backoff(GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)) {
119 if (args.pollset_set != nullptr) {
120 grpc_pollset_set_add_pollset_set(interested_parties_, args.pollset_set);
121 }
122 }
123
~NativeDnsResolver()124 NativeDnsResolver::~NativeDnsResolver() {
125 grpc_channel_args_destroy(channel_args_);
126 grpc_pollset_set_destroy(interested_parties_);
127 }
128
StartLocked()129 void NativeDnsResolver::StartLocked() { MaybeStartResolvingLocked(); }
130
RequestReresolutionLocked()131 void NativeDnsResolver::RequestReresolutionLocked() {
132 if (!resolving_) {
133 MaybeStartResolvingLocked();
134 }
135 }
136
ResetBackoffLocked()137 void NativeDnsResolver::ResetBackoffLocked() {
138 if (have_next_resolution_timer_) {
139 grpc_timer_cancel(&next_resolution_timer_);
140 }
141 backoff_.Reset();
142 }
143
ShutdownLocked()144 void NativeDnsResolver::ShutdownLocked() {
145 shutdown_ = true;
146 if (have_next_resolution_timer_) {
147 grpc_timer_cancel(&next_resolution_timer_);
148 }
149 }
150
OnNextResolution(void * arg,grpc_error * error)151 void NativeDnsResolver::OnNextResolution(void* arg, grpc_error* error) {
152 NativeDnsResolver* r = static_cast<NativeDnsResolver*>(arg);
153 GRPC_ERROR_REF(error); // ref owned by lambda
154 r->work_serializer_->Run([r, error]() { r->OnNextResolutionLocked(error); },
155 DEBUG_LOCATION);
156 }
157
OnNextResolutionLocked(grpc_error * error)158 void NativeDnsResolver::OnNextResolutionLocked(grpc_error* error) {
159 have_next_resolution_timer_ = false;
160 if (error == GRPC_ERROR_NONE && !resolving_) {
161 StartResolvingLocked();
162 }
163 Unref(DEBUG_LOCATION, "retry-timer");
164 GRPC_ERROR_UNREF(error);
165 }
166
OnResolved(void * arg,grpc_error * error)167 void NativeDnsResolver::OnResolved(void* arg, grpc_error* error) {
168 NativeDnsResolver* r = static_cast<NativeDnsResolver*>(arg);
169 GRPC_ERROR_REF(error); // owned by lambda
170 r->work_serializer_->Run([r, error]() { r->OnResolvedLocked(error); },
171 DEBUG_LOCATION);
172 }
173
OnResolvedLocked(grpc_error * error)174 void NativeDnsResolver::OnResolvedLocked(grpc_error* error) {
175 GPR_ASSERT(resolving_);
176 resolving_ = false;
177 if (shutdown_) {
178 Unref(DEBUG_LOCATION, "dns-resolving");
179 GRPC_ERROR_UNREF(error);
180 return;
181 }
182 if (addresses_ != nullptr) {
183 Result result;
184 for (size_t i = 0; i < addresses_->naddrs; ++i) {
185 result.addresses.emplace_back(&addresses_->addrs[i].addr,
186 addresses_->addrs[i].len,
187 nullptr /* args */);
188 }
189 grpc_resolved_addresses_destroy(addresses_);
190 result.args = grpc_channel_args_copy(channel_args_);
191 result_handler_->ReturnResult(std::move(result));
192 // Reset backoff state so that we start from the beginning when the
193 // next request gets triggered.
194 backoff_.Reset();
195 } else {
196 gpr_log(GPR_INFO, "dns resolution failed (will retry): %s",
197 grpc_error_string(error));
198 // Return transient error.
199 std::string error_message =
200 absl::StrCat("DNS resolution failed for service: ", name_to_resolve_);
201 result_handler_->ReturnError(grpc_error_set_int(
202 GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(error_message.c_str(),
203 &error, 1),
204 GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE));
205 // Set up for retry.
206 grpc_millis next_try = backoff_.NextAttemptTime();
207 grpc_millis timeout = next_try - ExecCtx::Get()->Now();
208 GPR_ASSERT(!have_next_resolution_timer_);
209 have_next_resolution_timer_ = true;
210 // TODO(roth): We currently deal with this ref manually. Once the
211 // new closure API is done, find a way to track this ref with the timer
212 // callback as part of the type system.
213 Ref(DEBUG_LOCATION, "next_resolution_timer").release();
214 if (timeout > 0) {
215 gpr_log(GPR_DEBUG, "retrying in %" PRId64 " milliseconds", timeout);
216 } else {
217 gpr_log(GPR_DEBUG, "retrying immediately");
218 }
219 GRPC_CLOSURE_INIT(&on_next_resolution_, NativeDnsResolver::OnNextResolution,
220 this, grpc_schedule_on_exec_ctx);
221 grpc_timer_init(&next_resolution_timer_, next_try, &on_next_resolution_);
222 }
223 Unref(DEBUG_LOCATION, "dns-resolving");
224 GRPC_ERROR_UNREF(error);
225 }
226
MaybeStartResolvingLocked()227 void NativeDnsResolver::MaybeStartResolvingLocked() {
228 // If there is an existing timer, the time it fires is the earliest time we
229 // can start the next resolution.
230 if (have_next_resolution_timer_) return;
231 if (last_resolution_timestamp_ >= 0) {
232 const grpc_millis earliest_next_resolution =
233 last_resolution_timestamp_ + min_time_between_resolutions_;
234 const grpc_millis ms_until_next_resolution =
235 earliest_next_resolution - grpc_core::ExecCtx::Get()->Now();
236 if (ms_until_next_resolution > 0) {
237 const grpc_millis last_resolution_ago =
238 grpc_core::ExecCtx::Get()->Now() - last_resolution_timestamp_;
239 gpr_log(GPR_DEBUG,
240 "In cooldown from last resolution (from %" PRId64
241 " ms ago). Will resolve again in %" PRId64 " ms",
242 last_resolution_ago, ms_until_next_resolution);
243 have_next_resolution_timer_ = true;
244 // TODO(roth): We currently deal with this ref manually. Once the
245 // new closure API is done, find a way to track this ref with the timer
246 // callback as part of the type system.
247 Ref(DEBUG_LOCATION, "next_resolution_timer_cooldown").release();
248 GRPC_CLOSURE_INIT(&on_next_resolution_,
249 NativeDnsResolver::OnNextResolution, this,
250 grpc_schedule_on_exec_ctx);
251 grpc_timer_init(&next_resolution_timer_,
252 ExecCtx::Get()->Now() + ms_until_next_resolution,
253 &on_next_resolution_);
254 return;
255 }
256 }
257 StartResolvingLocked();
258 }
259
StartResolvingLocked()260 void NativeDnsResolver::StartResolvingLocked() {
261 gpr_log(GPR_DEBUG, "Start resolving.");
262 // TODO(roth): We currently deal with this ref manually. Once the
263 // new closure API is done, find a way to track this ref with the timer
264 // callback as part of the type system.
265 Ref(DEBUG_LOCATION, "dns-resolving").release();
266 GPR_ASSERT(!resolving_);
267 resolving_ = true;
268 addresses_ = nullptr;
269 GRPC_CLOSURE_INIT(&on_resolved_, NativeDnsResolver::OnResolved, this,
270 grpc_schedule_on_exec_ctx);
271 grpc_resolve_address(name_to_resolve_.c_str(), kDefaultPort,
272 interested_parties_, &on_resolved_, &addresses_);
273 last_resolution_timestamp_ = grpc_core::ExecCtx::Get()->Now();
274 }
275
276 //
277 // Factory
278 //
279
280 class NativeDnsResolverFactory : public ResolverFactory {
281 public:
IsValidUri(const URI & uri) const282 bool IsValidUri(const URI& uri) const override {
283 if (GPR_UNLIKELY(!uri.authority().empty())) {
284 gpr_log(GPR_ERROR, "authority based dns uri's not supported");
285 return false;
286 }
287 return true;
288 }
289
CreateResolver(ResolverArgs args) const290 OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
291 if (!IsValidUri(args.uri)) return nullptr;
292 return MakeOrphanable<NativeDnsResolver>(std::move(args));
293 }
294
scheme() const295 const char* scheme() const override { return "dns"; }
296 };
297
298 } // namespace
299
300 } // namespace grpc_core
301
grpc_resolver_dns_native_init()302 void grpc_resolver_dns_native_init() {
303 grpc_core::UniquePtr<char> resolver =
304 GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
305 if (gpr_stricmp(resolver.get(), "native") == 0) {
306 gpr_log(GPR_DEBUG, "Using native dns resolver");
307 grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
308 absl::make_unique<grpc_core::NativeDnsResolverFactory>());
309 } else {
310 grpc_core::ResolverRegistry::Builder::InitRegistry();
311 grpc_core::ResolverFactory* existing_factory =
312 grpc_core::ResolverRegistry::LookupResolverFactory("dns");
313 if (existing_factory == nullptr) {
314 gpr_log(GPR_DEBUG, "Using native dns resolver");
315 grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
316 absl::make_unique<grpc_core::NativeDnsResolverFactory>());
317 }
318 }
319 }
320
grpc_resolver_dns_native_shutdown()321 void grpc_resolver_dns_native_shutdown() {}
322