• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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