blob: 4a264dad400d5a614fa370bc6677dd660d0eaf97 [file] [log] [blame]
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <grpc/support/port_platform.h>
#include <stdint.h>
#include <stdlib.h>
#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/types/optional.h"
#include <grpc/event_engine/event_engine.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/iomgr_fwd.h"
#include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/iomgr/resolved_address.h"
#include "src/core/lib/resolver/resolver.h"
#include "src/core/lib/resolver/resolver_factory.h"
#include "src/core/lib/service_config/service_config.h"
#include "src/core/lib/uri/uri_parser.h"
#if GRPC_ARES == 1
#include <stdio.h>
#include <address_sorting/address_sorting.h>
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_cat.h"
#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_balancer_addresses.h"
#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
#include "src/core/ext/filters/client_channel/resolver/polling_resolver.h"
#include "src/core/lib/backoff/backoff.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/config/config_vars.h"
#include "src/core/lib/event_engine/handle_containers.h"
#include "src/core/lib/iomgr/gethostname.h"
#include "src/core/lib/iomgr/resolve_address.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/json/json_reader.h"
#include "src/core/lib/json/json_writer.h"
#include "src/core/lib/resolver/server_address.h"
#include "src/core/lib/service_config/service_config_impl.h"
#include "src/core/lib/transport/error_utils.h"
#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6
#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120
#define GRPC_DNS_RECONNECT_JITTER 0.2
namespace grpc_core {
namespace {
class AresClientChannelDNSResolver : public PollingResolver {
public:
AresClientChannelDNSResolver(ResolverArgs args,
Duration min_time_between_resolutions);
OrphanablePtr<Orphanable> StartRequest() override;
private:
class AresRequestWrapper : public InternallyRefCounted<AresRequestWrapper> {
public:
explicit AresRequestWrapper(
RefCountedPtr<AresClientChannelDNSResolver> resolver)
: resolver_(std::move(resolver)) {
// TODO(hork): replace this callback bookkeeping with promises.
// Locking to prevent completion before all records are queried
MutexLock lock(&on_resolved_mu_);
Ref(DEBUG_LOCATION, "OnHostnameResolved").release();
GRPC_CLOSURE_INIT(&on_hostname_resolved_, OnHostnameResolved, this,
nullptr);
hostname_request_.reset(grpc_dns_lookup_hostname_ares(
resolver_->authority().c_str(), resolver_->name_to_resolve().c_str(),
kDefaultSecurePort, resolver_->interested_parties(),
&on_hostname_resolved_, &addresses_, resolver_->query_timeout_ms_));
GRPC_CARES_TRACE_LOG(
"resolver:%p Started resolving hostnames. hostname_request_:%p",
resolver_.get(), hostname_request_.get());
if (resolver_->enable_srv_queries_) {
Ref(DEBUG_LOCATION, "OnSRVResolved").release();
GRPC_CLOSURE_INIT(&on_srv_resolved_, OnSRVResolved, this, nullptr);
srv_request_.reset(grpc_dns_lookup_srv_ares(
resolver_->authority().c_str(),
resolver_->name_to_resolve().c_str(),
resolver_->interested_parties(), &on_srv_resolved_,
&balancer_addresses_, resolver_->query_timeout_ms_));
GRPC_CARES_TRACE_LOG(
"resolver:%p Started resolving SRV records. srv_request_:%p",
resolver_.get(), srv_request_.get());
}
if (resolver_->request_service_config_) {
Ref(DEBUG_LOCATION, "OnTXTResolved").release();
GRPC_CLOSURE_INIT(&on_txt_resolved_, OnTXTResolved, this, nullptr);
txt_request_.reset(grpc_dns_lookup_txt_ares(
resolver_->authority().c_str(),
resolver_->name_to_resolve().c_str(),
resolver_->interested_parties(), &on_txt_resolved_,
&service_config_json_, resolver_->query_timeout_ms_));
GRPC_CARES_TRACE_LOG(
"resolver:%p Started resolving TXT records. txt_request_:%p",
resolver_.get(), txt_request_.get());
}
}
~AresRequestWrapper() override {
gpr_free(service_config_json_);
resolver_.reset(DEBUG_LOCATION, "dns-resolving");
}
// Note that thread safety cannot be analyzed due to this being invoked from
// OrphanablePtr<>, and there's no way to pass the lock annotation through
// there.
void Orphan() override ABSL_NO_THREAD_SAFETY_ANALYSIS {
{
MutexLock lock(&on_resolved_mu_);
if (hostname_request_ != nullptr) {
grpc_cancel_ares_request(hostname_request_.get());
}
if (srv_request_ != nullptr) {
grpc_cancel_ares_request(srv_request_.get());
}
if (txt_request_ != nullptr) {
grpc_cancel_ares_request(txt_request_.get());
}
}
Unref(DEBUG_LOCATION, "Orphan");
}
private:
static void OnHostnameResolved(void* arg, grpc_error_handle error);
static void OnSRVResolved(void* arg, grpc_error_handle error);
static void OnTXTResolved(void* arg, grpc_error_handle error);
absl::optional<Result> OnResolvedLocked(grpc_error_handle error)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(on_resolved_mu_);
Mutex on_resolved_mu_;
RefCountedPtr<AresClientChannelDNSResolver> resolver_;
grpc_closure on_hostname_resolved_;
std::unique_ptr<grpc_ares_request> hostname_request_
ABSL_GUARDED_BY(on_resolved_mu_);
grpc_closure on_srv_resolved_;
std::unique_ptr<grpc_ares_request> srv_request_
ABSL_GUARDED_BY(on_resolved_mu_);
grpc_closure on_txt_resolved_;
std::unique_ptr<grpc_ares_request> txt_request_
ABSL_GUARDED_BY(on_resolved_mu_);
// Output fields from ares request.
std::unique_ptr<ServerAddressList> addresses_
ABSL_GUARDED_BY(on_resolved_mu_);
std::unique_ptr<ServerAddressList> balancer_addresses_
ABSL_GUARDED_BY(on_resolved_mu_);
char* service_config_json_ ABSL_GUARDED_BY(on_resolved_mu_) = nullptr;
};
~AresClientChannelDNSResolver() override;
/// whether to request the service config
const bool request_service_config_;
// whether or not to enable SRV DNS queries
const bool enable_srv_queries_;
// timeout in milliseconds for active DNS queries
const int query_timeout_ms_;
};
AresClientChannelDNSResolver::AresClientChannelDNSResolver(
ResolverArgs args, Duration min_time_between_resolutions)
: PollingResolver(std::move(args), min_time_between_resolutions,
BackOff::Options()
.set_initial_backoff(Duration::Milliseconds(
GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * 1000))
.set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER)
.set_jitter(GRPC_DNS_RECONNECT_JITTER)
.set_max_backoff(Duration::Milliseconds(
GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)),
&grpc_trace_cares_resolver),
request_service_config_(
!channel_args()
.GetBool(GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION)
.value_or(true)),
enable_srv_queries_(channel_args()
.GetBool(GRPC_ARG_DNS_ENABLE_SRV_QUERIES)
.value_or(false)),
query_timeout_ms_(
std::max(0, channel_args()
.GetInt(GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS)
.value_or(GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS))) {}
AresClientChannelDNSResolver::~AresClientChannelDNSResolver() {
GRPC_CARES_TRACE_LOG("resolver:%p destroying AresClientChannelDNSResolver",
this);
}
OrphanablePtr<Orphanable> AresClientChannelDNSResolver::StartRequest() {
return MakeOrphanable<AresRequestWrapper>(
Ref(DEBUG_LOCATION, "dns-resolving"));
}
bool ValueInJsonArray(const Json::Array& array, const char* value) {
for (const Json& entry : array) {
if (entry.type() == Json::Type::kString && entry.string() == value) {
return true;
}
}
return false;
}
std::string ChooseServiceConfig(char* service_config_choice_json,
grpc_error_handle* error) {
auto json = JsonParse(service_config_choice_json);
if (!json.ok()) {
*error = absl_status_to_grpc_error(json.status());
return "";
}
if (json->type() != Json::Type::kArray) {
*error = GRPC_ERROR_CREATE(
"Service Config Choices, error: should be of type array");
return "";
}
const Json* service_config = nullptr;
std::vector<grpc_error_handle> error_list;
for (const Json& choice : json->array()) {
if (choice.type() != Json::Type::kObject) {
error_list.push_back(GRPC_ERROR_CREATE(
"Service Config Choice, error: should be of type object"));
continue;
}
// Check client language, if specified.
auto it = choice.object().find("clientLanguage");
if (it != choice.object().end()) {
if (it->second.type() != Json::Type::kArray) {
error_list.push_back(GRPC_ERROR_CREATE(
"field:clientLanguage error:should be of type array"));
} else if (!ValueInJsonArray(it->second.array(), "c++")) {
continue;
}
}
// Check client hostname, if specified.
it = choice.object().find("clientHostname");
if (it != choice.object().end()) {
if (it->second.type() != Json::Type::kArray) {
error_list.push_back(GRPC_ERROR_CREATE(
"field:clientHostname error:should be of type array"));
} else {
char* hostname = grpc_gethostname();
if (hostname == nullptr ||
!ValueInJsonArray(it->second.array(), hostname)) {
continue;
}
}
}
// Check percentage, if specified.
it = choice.object().find("percentage");
if (it != choice.object().end()) {
if (it->second.type() != Json::Type::kNumber) {
error_list.push_back(GRPC_ERROR_CREATE(
"field:percentage error:should be of type number"));
} else {
int random_pct = rand() % 100;
int percentage;
if (sscanf(it->second.string().c_str(), "%d", &percentage) != 1) {
error_list.push_back(GRPC_ERROR_CREATE(
"field:percentage error:should be of type integer"));
} else if (random_pct > percentage || percentage == 0) {
continue;
}
}
}
// Found service config.
it = choice.object().find("serviceConfig");
if (it == choice.object().end()) {
error_list.push_back(GRPC_ERROR_CREATE(
"field:serviceConfig error:required field missing"));
} else if (it->second.type() != Json::Type::kObject) {
error_list.push_back(GRPC_ERROR_CREATE(
"field:serviceConfig error:should be of type object"));
} else if (service_config == nullptr) {
service_config = &it->second;
}
}
if (!error_list.empty()) {
service_config = nullptr;
*error = GRPC_ERROR_CREATE_FROM_VECTOR("Service Config Choices Parser",
&error_list);
}
if (service_config == nullptr) return "";
return JsonDump(*service_config);
}
void AresClientChannelDNSResolver::AresRequestWrapper::OnHostnameResolved(
void* arg, grpc_error_handle error) {
auto* self = static_cast<AresRequestWrapper*>(arg);
absl::optional<Result> result;
{
MutexLock lock(&self->on_resolved_mu_);
self->hostname_request_.reset();
result = self->OnResolvedLocked(error);
}
if (result.has_value()) {
self->resolver_->OnRequestComplete(std::move(*result));
}
self->Unref(DEBUG_LOCATION, "OnHostnameResolved");
}
void AresClientChannelDNSResolver::AresRequestWrapper::OnSRVResolved(
void* arg, grpc_error_handle error) {
auto* self = static_cast<AresRequestWrapper*>(arg);
absl::optional<Result> result;
{
MutexLock lock(&self->on_resolved_mu_);
self->srv_request_.reset();
result = self->OnResolvedLocked(error);
}
if (result.has_value()) {
self->resolver_->OnRequestComplete(std::move(*result));
}
self->Unref(DEBUG_LOCATION, "OnSRVResolved");
}
void AresClientChannelDNSResolver::AresRequestWrapper::OnTXTResolved(
void* arg, grpc_error_handle error) {
auto* self = static_cast<AresRequestWrapper*>(arg);
absl::optional<Result> result;
{
MutexLock lock(&self->on_resolved_mu_);
self->txt_request_.reset();
result = self->OnResolvedLocked(error);
}
if (result.has_value()) {
self->resolver_->OnRequestComplete(std::move(*result));
}
self->Unref(DEBUG_LOCATION, "OnTXTResolved");
}
// Returns a Result if resolution is complete.
// callers must release the lock and call OnRequestComplete if a Result is
// returned. This is because OnRequestComplete may Orphan the resolver, which
// requires taking the lock.
absl::optional<AresClientChannelDNSResolver::Result>
AresClientChannelDNSResolver::AresRequestWrapper::OnResolvedLocked(
grpc_error_handle error) ABSL_EXCLUSIVE_LOCKS_REQUIRED(on_resolved_mu_) {
if (hostname_request_ != nullptr || srv_request_ != nullptr ||
txt_request_ != nullptr) {
GRPC_CARES_TRACE_LOG(
"resolver:%p OnResolved() waiting for results (hostname: %s, srv: %s, "
"txt: %s)",
this, hostname_request_ != nullptr ? "waiting" : "done",
srv_request_ != nullptr ? "waiting" : "done",
txt_request_ != nullptr ? "waiting" : "done");
return absl::nullopt;
}
GRPC_CARES_TRACE_LOG("resolver:%p OnResolved() proceeding", this);
Result result;
result.args = resolver_->channel_args();
// TODO(roth): Change logic to be able to report failures for addresses
// and service config independently of each other.
if (addresses_ != nullptr || balancer_addresses_ != nullptr) {
if (addresses_ != nullptr) {
result.addresses = std::move(*addresses_);
} else {
result.addresses = ServerAddressList();
}
if (service_config_json_ != nullptr) {
grpc_error_handle service_config_error;
std::string service_config_string =
ChooseServiceConfig(service_config_json_, &service_config_error);
if (!service_config_error.ok()) {
result.service_config = absl::UnavailableError(
absl::StrCat("failed to parse service config: ",
StatusToString(service_config_error)));
} else if (!service_config_string.empty()) {
GRPC_CARES_TRACE_LOG("resolver:%p selected service config choice: %s",
this, service_config_string.c_str());
result.service_config = ServiceConfigImpl::Create(
resolver_->channel_args(), service_config_string);
if (!result.service_config.ok()) {
result.service_config = absl::UnavailableError(
absl::StrCat("failed to parse service config: ",
result.service_config.status().message()));
}
}
}
if (balancer_addresses_ != nullptr) {
result.args = SetGrpcLbBalancerAddresses(
result.args, ServerAddressList(*balancer_addresses_));
}
} else {
GRPC_CARES_TRACE_LOG("resolver:%p dns resolution failed: %s", this,
StatusToString(error).c_str());
std::string error_message;
grpc_error_get_str(error, StatusStrProperty::kDescription, &error_message);
absl::Status status = absl::UnavailableError(
absl::StrCat("DNS resolution failed for ", resolver_->name_to_resolve(),
": ", error_message));
result.addresses = status;
result.service_config = status;
}
return std::move(result);
}
//
// Factory
//
class AresClientChannelDNSResolverFactory : public ResolverFactory {
public:
absl::string_view scheme() const override { return "dns"; }
bool IsValidUri(const URI& uri) const override {
if (absl::StripPrefix(uri.path(), "/").empty()) {
gpr_log(GPR_ERROR, "no server name supplied in dns URI");
return false;
}
return true;
}
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
Duration min_time_between_resolutions = std::max(
Duration::Zero(), args.args
.GetDurationFromIntMillis(
GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS)
.value_or(Duration::Seconds(30)));
return MakeOrphanable<AresClientChannelDNSResolver>(
std::move(args), min_time_between_resolutions);
}
};
class AresDNSResolver : public DNSResolver {
public:
// Abstract class that centralizes common request handling logic via the
// template method pattern.
// This requires a two-phase initialization, where 1) a request is created via
// a subclass constructor, and 2) the request is initiated via Run()
class AresRequest {
public:
virtual ~AresRequest() {
GRPC_CARES_TRACE_LOG("AresRequest:%p dtor ares_request_:%p", this,
grpc_ares_request_.get());
resolver_->UnregisterRequest(task_handle());
grpc_pollset_set_destroy(pollset_set_);
}
// Initiates the low-level c-ares request and returns its handle.
virtual std::unique_ptr<grpc_ares_request> MakeRequestLocked() = 0;
// Called on ares resolution, but not upon cancellation.
// After execution, the AresRequest will perform any final cleanup and
// delete itself.
virtual void OnComplete(grpc_error_handle error) = 0;
// Called to initiate the request.
void Run() {
MutexLock lock(&mu_);
grpc_ares_request_ = MakeRequestLocked();
}
bool Cancel() {
MutexLock lock(&mu_);
if (grpc_ares_request_ != nullptr) {
GRPC_CARES_TRACE_LOG("AresRequest:%p Cancel ares_request_:%p", this,
grpc_ares_request_.get());
if (completed_) return false;
// OnDnsLookupDone will still be run
completed_ = true;
grpc_cancel_ares_request(grpc_ares_request_.get());
} else {
completed_ = true;
OnDnsLookupDone(this, absl::CancelledError());
}
grpc_pollset_set_del_pollset_set(pollset_set_, interested_parties_);
return true;
}
TaskHandle task_handle() {
return {reinterpret_cast<intptr_t>(this), aba_token_};
}
protected:
AresRequest(absl::string_view name, absl::string_view name_server,
Duration timeout, grpc_pollset_set* interested_parties,
AresDNSResolver* resolver, intptr_t aba_token)
: name_(name),
name_server_(name_server),
timeout_(timeout),
interested_parties_(interested_parties),
completed_(false),
resolver_(resolver),
aba_token_(aba_token),
pollset_set_(grpc_pollset_set_create()) {
GRPC_CLOSURE_INIT(&on_dns_lookup_done_, OnDnsLookupDone, this,
grpc_schedule_on_exec_ctx);
grpc_pollset_set_add_pollset_set(pollset_set_, interested_parties_);
}
grpc_pollset_set* pollset_set() { return pollset_set_; };
grpc_closure* on_dns_lookup_done() { return &on_dns_lookup_done_; };
const std::string& name() { return name_; }
const std::string& name_server() { return name_server_; }
const Duration& timeout() { return timeout_; }
private:
// Called by ares when lookup has completed or when cancelled. It is always
// called exactly once, and it triggers self-deletion.
static void OnDnsLookupDone(void* arg, grpc_error_handle error) {
AresRequest* r = static_cast<AresRequest*>(arg);
auto deleter = std::unique_ptr<AresRequest>(r);
{
MutexLock lock(&r->mu_);
grpc_pollset_set_del_pollset_set(r->pollset_set_,
r->interested_parties_);
if (r->completed_) {
return;
}
r->completed_ = true;
}
r->OnComplete(error);
}
// the name to resolve
const std::string name_;
// the name server to query
const std::string name_server_;
// request-specific timeout
Duration timeout_;
// mutex to synchronize access to this object (but not to the ares_request
// object itself).
Mutex mu_;
// parties interested in our I/O
grpc_pollset_set* const interested_parties_;
// underlying cares_request that the query is performed on
std::unique_ptr<grpc_ares_request> grpc_ares_request_ ABSL_GUARDED_BY(mu_);
// Set when the callback is either cancelled or executed.
// It is not the subclasses' responsibility to set this flag.
bool completed_ ABSL_GUARDED_BY(mu_);
// Parent resolver that created this request
AresDNSResolver* resolver_;
// Unique token to help distinguish this request from others that may later
// be created in the same memory location.
intptr_t aba_token_;
// closure to call when the ares resolution request completes. Subclasses
// should use this as the ares callback in MakeRequestLocked()
grpc_closure on_dns_lookup_done_ ABSL_GUARDED_BY(mu_);
// locally owned pollset_set, required to support cancellation of requests
// while ares still needs a valid pollset_set. Subclasses should give this
// pollset to ares in MakeRequestLocked();
grpc_pollset_set* pollset_set_;
};
class AresHostnameRequest : public AresRequest {
public:
AresHostnameRequest(
absl::string_view name, absl::string_view default_port,
absl::string_view name_server, Duration timeout,
grpc_pollset_set* interested_parties,
std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
on_resolve_address_done,
AresDNSResolver* resolver, intptr_t aba_token)
: AresRequest(name, name_server, timeout, interested_parties, resolver,
aba_token),
default_port_(default_port),
on_resolve_address_done_(std::move(on_resolve_address_done)) {
GRPC_CARES_TRACE_LOG("AresHostnameRequest:%p ctor", this);
}
std::unique_ptr<grpc_ares_request> MakeRequestLocked() override {
auto ares_request =
std::unique_ptr<grpc_ares_request>(grpc_dns_lookup_hostname_ares(
name_server().c_str(), name().c_str(), default_port_.c_str(),
pollset_set(), on_dns_lookup_done(), &addresses_,
timeout().millis()));
GRPC_CARES_TRACE_LOG("AresHostnameRequest:%p Start ares_request_:%p",
this, ares_request.get());
return ares_request;
}
void OnComplete(grpc_error_handle error) override {
GRPC_CARES_TRACE_LOG("AresHostnameRequest:%p OnComplete", this);
if (!error.ok()) {
on_resolve_address_done_(grpc_error_to_absl_status(error));
return;
}
std::vector<grpc_resolved_address> resolved_addresses;
if (addresses_ != nullptr) {
resolved_addresses.reserve(addresses_->size());
for (const auto& server_address : *addresses_) {
resolved_addresses.push_back(server_address.address());
}
}
on_resolve_address_done_(std::move(resolved_addresses));
}
// the default port to use if name doesn't have one
const std::string default_port_;
// user-provided completion callback
const std::function<void(
absl::StatusOr<std::vector<grpc_resolved_address>>)>
on_resolve_address_done_;
// currently resolving addresses
std::unique_ptr<ServerAddressList> addresses_;
};
class AresSRVRequest : public AresRequest {
public:
AresSRVRequest(
absl::string_view name, absl::string_view name_server, Duration timeout,
grpc_pollset_set* interested_parties,
std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
on_resolve_address_done,
AresDNSResolver* resolver, intptr_t aba_token)
: AresRequest(name, name_server, timeout, interested_parties, resolver,
aba_token),
on_resolve_address_done_(std::move(on_resolve_address_done)) {
GRPC_CARES_TRACE_LOG("AresSRVRequest:%p ctor", this);
}
std::unique_ptr<grpc_ares_request> MakeRequestLocked() override {
auto ares_request =
std::unique_ptr<grpc_ares_request>(grpc_dns_lookup_srv_ares(
name_server().c_str(), name().c_str(), pollset_set(),
on_dns_lookup_done(), &balancer_addresses_, timeout().millis()));
GRPC_CARES_TRACE_LOG("AresSRVRequest:%p Start ares_request_:%p", this,
ares_request.get());
return ares_request;
}
void OnComplete(grpc_error_handle error) override {
GRPC_CARES_TRACE_LOG("AresSRVRequest:%p OnComplete", this);
if (!error.ok()) {
on_resolve_address_done_(grpc_error_to_absl_status(error));
return;
}
std::vector<grpc_resolved_address> resolved_addresses;
if (balancer_addresses_ != nullptr) {
resolved_addresses.reserve(balancer_addresses_->size());
for (const auto& server_address : *balancer_addresses_) {
resolved_addresses.push_back(server_address.address());
}
}
on_resolve_address_done_(std::move(resolved_addresses));
}
// user-provided completion callback
const std::function<void(
absl::StatusOr<std::vector<grpc_resolved_address>>)>
on_resolve_address_done_;
// currently resolving addresses
std::unique_ptr<ServerAddressList> balancer_addresses_;
};
class AresTXTRequest : public AresRequest {
public:
AresTXTRequest(absl::string_view name, absl::string_view name_server,
Duration timeout, grpc_pollset_set* interested_parties,
std::function<void(absl::StatusOr<std::string>)> on_resolved,
AresDNSResolver* resolver, intptr_t aba_token)
: AresRequest(name, name_server, timeout, interested_parties, resolver,
aba_token),
on_resolved_(std::move(on_resolved)) {
GRPC_CARES_TRACE_LOG("AresTXTRequest:%p ctor", this);
}
~AresTXTRequest() override { gpr_free(service_config_json_); }
std::unique_ptr<grpc_ares_request> MakeRequestLocked() override {
auto ares_request =
std::unique_ptr<grpc_ares_request>(grpc_dns_lookup_txt_ares(
name_server().c_str(), name().c_str(), pollset_set(),
on_dns_lookup_done(), &service_config_json_, timeout().millis()));
GRPC_CARES_TRACE_LOG("AresSRVRequest:%p Start ares_request_:%p", this,
ares_request.get());
return ares_request;
}
void OnComplete(grpc_error_handle error) override {
GRPC_CARES_TRACE_LOG("AresSRVRequest:%p OnComplete", this);
if (!error.ok()) {
on_resolved_(grpc_error_to_absl_status(error));
return;
}
on_resolved_(service_config_json_);
}
// service config from the TXT record
char* service_config_json_ = nullptr;
// user-provided completion callback
const std::function<void(absl::StatusOr<std::string>)> on_resolved_;
};
TaskHandle LookupHostname(
std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
on_resolved,
absl::string_view name, absl::string_view default_port, Duration timeout,
grpc_pollset_set* interested_parties,
absl::string_view name_server) override {
MutexLock lock(&mu_);
auto* request = new AresHostnameRequest(
name, default_port, name_server, timeout, interested_parties,
std::move(on_resolved), this, aba_token_++);
request->Run();
auto handle = request->task_handle();
open_requests_.insert(handle);
return handle;
}
absl::StatusOr<std::vector<grpc_resolved_address>> LookupHostnameBlocking(
absl::string_view name, absl::string_view default_port) override {
// TODO(apolcyn): change this to wrap the async version of the c-ares
// API with a promise, and remove the reference to the previous resolver.
return default_resolver_->LookupHostnameBlocking(name, default_port);
}
TaskHandle LookupSRV(
std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
on_resolved,
absl::string_view name, Duration timeout,
grpc_pollset_set* interested_parties,
absl::string_view name_server) override {
MutexLock lock(&mu_);
auto* request =
new AresSRVRequest(name, name_server, timeout, interested_parties,
std::move(on_resolved), this, aba_token_++);
request->Run();
auto handle = request->task_handle();
open_requests_.insert(handle);
return handle;
};
TaskHandle LookupTXT(
std::function<void(absl::StatusOr<std::string>)> on_resolved,
absl::string_view name, Duration timeout,
grpc_pollset_set* interested_parties,
absl::string_view name_server) override {
MutexLock lock(&mu_);
auto* request =
new AresTXTRequest(name, name_server, timeout, interested_parties,
std::move(on_resolved), this, aba_token_++);
request->Run();
auto handle = request->task_handle();
open_requests_.insert(handle);
return handle;
};
bool Cancel(TaskHandle handle) override {
MutexLock lock(&mu_);
if (!open_requests_.contains(handle)) {
// Unknown request, possibly completed already, or an invalid handle.
GRPC_CARES_TRACE_LOG(
"AresDNSResolver:%p attempt to cancel unknown TaskHandle:%s", this,
HandleToString(handle).c_str());
return false;
}
auto* request = reinterpret_cast<AresRequest*>(handle.keys[0]);
GRPC_CARES_TRACE_LOG("AresDNSResolver:%p cancel ares_request:%p", this,
request);
return request->Cancel();
}
private:
// Called exclusively from the AresRequest destructor.
void UnregisterRequest(TaskHandle handle) {
MutexLock lock(&mu_);
open_requests_.erase(handle);
}
// the previous default DNS resolver, used to delegate blocking DNS calls to
std::shared_ptr<DNSResolver> default_resolver_ = GetDNSResolver();
Mutex mu_;
grpc_event_engine::experimental::LookupTaskHandleSet open_requests_
ABSL_GUARDED_BY(mu_);
intptr_t aba_token_ ABSL_GUARDED_BY(mu_) = 0;
};
bool ShouldUseAres(absl::string_view resolver_env) {
return resolver_env.empty() || absl::EqualsIgnoreCase(resolver_env, "ares");
}
bool UseAresDnsResolver() {
return ShouldUseAres(ConfigVars::Get().DnsResolver());
}
} // namespace
void RegisterAresDnsResolver(CoreConfiguration::Builder* builder) {
if (UseAresDnsResolver()) {
builder->resolver_registry()->RegisterResolverFactory(
std::make_unique<AresClientChannelDNSResolverFactory>());
}
}
} // namespace grpc_core
void grpc_resolver_dns_ares_init() {
if (grpc_core::UseAresDnsResolver()) {
address_sorting_init();
grpc_error_handle error = grpc_ares_init();
if (!error.ok()) {
GRPC_LOG_IF_ERROR("grpc_ares_init() failed", error);
return;
}
grpc_core::ResetDNSResolver(std::make_unique<grpc_core::AresDNSResolver>());
}
}
void grpc_resolver_dns_ares_shutdown() {
if (grpc_core::UseAresDnsResolver()) {
address_sorting_shutdown();
grpc_ares_cleanup();
}
}
#else // GRPC_ARES == 1
namespace grpc_core {
void RegisterAresDnsResolver(CoreConfiguration::Builder*) {}
} // namespace grpc_core
void grpc_resolver_dns_ares_init() {}
void grpc_resolver_dns_ares_shutdown() {}
#endif // GRPC_ARES == 1