Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 44 additions & 11 deletions source/common/network/apple_dns_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -113,22 +113,55 @@ ActiveDnsQuery* AppleDnsResolverImpl::resolve(const std::string& dns_name,
DnsLookupFamily dns_lookup_family,
ResolveCb callback) {
ENVOY_LOG(debug, "DNS resolver resolve={}", dns_name);
std::unique_ptr<PendingResolution> pending_resolution(
new PendingResolution(*this, callback, dispatcher_, main_sd_ref_, dns_name));

DNSServiceErrorType error = pending_resolution->dnsServiceGetAddrInfo(dns_lookup_family);
if (error != kDNSServiceErr_NoError) {
ENVOY_LOG(warn, "DNS resolver error ({}) in dnsServiceGetAddrInfo for {}", error, dns_name);
return nullptr;
Address::InstanceConstSharedPtr address{};
try {
// After production testing with this resolver implementation it became empirically demonstrable
Comment thread
mattklein123 marked this conversation as resolved.
Outdated
// that Apple's API issues queries to the DNS server for dns_name that might already be an IP
// address. In contrast, c-ares synchronously resolves such cases. Moreover, some DNS servers
// might not resolve IP addresses and thus result in callback targets that never get a

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think phrasing it this way is a little bit backwards. Even if we have an example of a DNS server that does "resolve" an IP address, I wouldn't bet on this being standard or expected. I think this comment can probably be truncated to something along the lines of, "When an IP address is submitted to c-ares it synchronously returns it without submitting a DNS query. Because we've come to rely on this behavior, this resolver implements a similar resolution path to avoid making improper DNS queries for resolved IPs."

// resolution. Therefore, we short circuit here by trying to parse the dns_name into an internet
// address and synchronously issue the ResolveCb; only if the parsing throws an exception the
// resolver issues a call to Apple's API.
address = Utility::parseInternetAddress(dns_name);
ENVOY_LOG(debug, "DNS resolver resolved ({}) to ({}) without issuing call to Apple API",
dns_name, address->asString());
} catch (const EnvoyException& e) {
// Resolution via Apple APIs
ENVOY_LOG(debug, "DNS resolver local resolution failed with: {}", e.what());
std::unique_ptr<PendingResolution> pending_resolution(
new PendingResolution(*this, callback, dispatcher_, main_sd_ref_, dns_name));

DNSServiceErrorType error = pending_resolution->dnsServiceGetAddrInfo(dns_lookup_family);
if (error != kDNSServiceErr_NoError) {
ENVOY_LOG(warn, "DNS resolver error ({}) in dnsServiceGetAddrInfo for {}", error, dns_name);
return nullptr;
}

// If the query was synchronously resolved in the Apple API call, there is no need to return the
// query.
if (pending_resolution->synchronously_completed_) {
return nullptr;
}

pending_resolution->owned_ = true;
return pending_resolution.release();
}

// If the query was synchronously resolved, there is no need to return the query.
if (pending_resolution->synchronously_completed_) {
ASSERT(address != nullptr);
// Finish local, synchronous resolution. This needs to happen outside of the exception block above
// as the callback itself can throw.
try {
callback(DnsResolver::ResolutionStatus::Success,
{DnsResponse(address, std::chrono::seconds(60))});
return nullptr;
} catch (const std::exception& e) {
ENVOY_LOG(warn, "std::exception in callback with local resolution: {}", e.what());
throw EnvoyException(e.what());
} catch (...) {
ENVOY_LOG(warn, "Unknown exception in callback with local resolution");
throw EnvoyException("unknown");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this try catch needed vs. just letting the callback through if that is what it does?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was imitating the c-ares resolver which catches other exception types and throws them as EnvoyExceptions (plus post the throw). But, yeah if we don't mind about the exception type I can just call the callback without the try.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we are calling code we own, I don't know why we would be catching other types and wrapping them, so I would just remove all of this.

}

pending_resolution->owned_ = true;
return pending_resolution.release();
}

void AppleDnsResolverImpl::addPendingQuery(PendingResolution* query) {
Expand Down
1 change: 1 addition & 0 deletions source/common/network/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class Utility {
* Resolve a URL.
* @param url supplies the url to resolve.
* @return Address::InstanceConstSharedPtr the resolved address.
* @throw EnvoyException if url is invalid.
*/
static Address::InstanceConstSharedPtr resolveUrl(const std::string& url);

Expand Down
40 changes: 36 additions & 4 deletions test/common/network/apple_dns_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -154,26 +154,45 @@ TEST_F(AppleDnsImplTest, DnsIpAddressVersion) {
}

TEST_F(AppleDnsImplTest, CallbackException) {
EXPECT_NE(nullptr, resolveWithException<EnvoyException>("1.2.3.4", DnsLookupFamily::V4Only,
EXPECT_NE(nullptr, resolveWithException<EnvoyException>("google.com", DnsLookupFamily::V4Only,
EnvoyException("Envoy exception")));
EXPECT_THROW_WITH_MESSAGE(dispatcher_->run(Event::Dispatcher::RunType::Block), EnvoyException,
"Envoy exception");
}

TEST_F(AppleDnsImplTest, CallbackException2) {
EXPECT_NE(nullptr, resolveWithException<std::runtime_error>("1.2.3.4", DnsLookupFamily::V4Only,
EXPECT_NE(nullptr, resolveWithException<std::runtime_error>("google.com", DnsLookupFamily::V4Only,
std::runtime_error("runtime error")));
EXPECT_THROW_WITH_MESSAGE(dispatcher_->run(Event::Dispatcher::RunType::Block), EnvoyException,
"runtime error");
}

TEST_F(AppleDnsImplTest, CallbackException3) {
EXPECT_NE(nullptr,
resolveWithException<std::string>("1.2.3.4", DnsLookupFamily::V4Only, std::string()));
EXPECT_NE(nullptr, resolveWithException<std::string>("google.com", DnsLookupFamily::V4Only,
std::string()));
EXPECT_THROW_WITH_MESSAGE(dispatcher_->run(Event::Dispatcher::RunType::Block), EnvoyException,
"unknown");
}

TEST_F(AppleDnsImplTest, CallbackExceptionLocalResolution) {
EXPECT_THROW_WITH_MESSAGE(resolveWithException<EnvoyException>("1.2.3.4", DnsLookupFamily::V4Only,
EnvoyException("Envoy exception")),
EnvoyException, "Envoy exception");
}

TEST_F(AppleDnsImplTest, CallbackExceptionLocalResolution2) {
EXPECT_THROW_WITH_MESSAGE(
resolveWithException<std::runtime_error>("1.2.3.4", DnsLookupFamily::V4Only,
std::runtime_error("runtime error")),
EnvoyException, "runtime error");
}

TEST_F(AppleDnsImplTest, CallbackExceptionLocalResolution3) {
EXPECT_THROW_WITH_MESSAGE(
resolveWithException<std::string>("1.2.3.4", DnsLookupFamily::V4Only, std::string()),
EnvoyException, "unknown");
}

// Validate working of cancellation provided by ActiveDnsQuery return.
TEST_F(AppleDnsImplTest, Cancel) {
ActiveDnsQuery* query =
Expand All @@ -194,6 +213,19 @@ TEST_F(AppleDnsImplTest, Timeout) {
dispatcher_->run(Event::Dispatcher::RunType::Block);
}

TEST_F(AppleDnsImplTest, LocalResolution) {
auto pending_resolution = resolver_->resolve(
"0.0.0.0", DnsLookupFamily::Auto,
[](DnsResolver::ResolutionStatus status, std::list<DnsResponse>&& results) -> void {
EXPECT_EQ(DnsResolver::ResolutionStatus::Success, status);
EXPECT_EQ(1, results.size());
EXPECT_EQ("0.0.0.0:0", results.front().address_->asString());
EXPECT_EQ(std::chrono::seconds(60), results.front().ttl_);
});
EXPECT_EQ(nullptr, pending_resolution);
// Note that the dispatcher does NOT have to run because resolution is synchronous.
}

// This class compliments the tests above by using a mocked Apple API that allows finer control over
// error conditions, and callback firing.
class AppleDnsImplFakeApiTest : public testing::Test {
Expand Down