|
|
|
@ -21,6 +21,11 @@ |
|
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
|
|
#include <address_sorting/address_sorting.h> |
|
|
|
|
#include <gmock/gmock.h> |
|
|
|
|
#include <gtest/gtest.h> |
|
|
|
|
|
|
|
|
|
#include "absl/functional/bind_front.h" |
|
|
|
|
#include "absl/strings/match.h" |
|
|
|
|
|
|
|
|
|
#include <grpc/grpc.h> |
|
|
|
|
#include <grpc/support/alloc.h> |
|
|
|
@ -28,6 +33,7 @@ |
|
|
|
|
#include <grpc/support/sync.h> |
|
|
|
|
#include <grpc/support/time.h> |
|
|
|
|
|
|
|
|
|
#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" |
|
|
|
|
#include "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h" |
|
|
|
|
#include "src/core/lib/event_engine/sockaddr.h" |
|
|
|
|
#include "src/core/lib/gpr/string.h" |
|
|
|
@ -35,368 +41,397 @@ |
|
|
|
|
#include "src/core/lib/iomgr/executor.h" |
|
|
|
|
#include "src/core/lib/iomgr/iomgr.h" |
|
|
|
|
#include "test/core/util/cmdline.h" |
|
|
|
|
#include "test/core/util/fake_udp_and_tcp_server.h" |
|
|
|
|
#include "test/core/util/test_config.h" |
|
|
|
|
#include "test/cpp/util/test_config.h" |
|
|
|
|
|
|
|
|
|
static gpr_timespec test_deadline(void) { |
|
|
|
|
return grpc_timeout_seconds_to_deadline(100); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
typedef struct args_struct { |
|
|
|
|
gpr_event ev; |
|
|
|
|
grpc_resolved_addresses* addrs; |
|
|
|
|
gpr_mu* mu; |
|
|
|
|
bool done; // guarded by mu
|
|
|
|
|
grpc_pollset* pollset; // guarded by mu
|
|
|
|
|
grpc_pollset_set* pollset_set; |
|
|
|
|
} args_struct; |
|
|
|
|
|
|
|
|
|
static void do_nothing(void* /*arg*/, grpc_error_handle /*error*/) {} |
|
|
|
|
|
|
|
|
|
void args_init(args_struct* args) { |
|
|
|
|
gpr_event_init(&args->ev); |
|
|
|
|
args->pollset = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size())); |
|
|
|
|
grpc_pollset_init(args->pollset, &args->mu); |
|
|
|
|
args->pollset_set = grpc_pollset_set_create(); |
|
|
|
|
grpc_pollset_set_add_pollset(args->pollset_set, args->pollset); |
|
|
|
|
args->addrs = nullptr; |
|
|
|
|
args->done = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void args_finish(args_struct* args) { |
|
|
|
|
GPR_ASSERT(gpr_event_wait(&args->ev, test_deadline())); |
|
|
|
|
grpc_resolved_addresses_destroy(args->addrs); |
|
|
|
|
grpc_pollset_set_del_pollset(args->pollset_set, args->pollset); |
|
|
|
|
grpc_pollset_set_destroy(args->pollset_set); |
|
|
|
|
grpc_closure do_nothing_cb; |
|
|
|
|
GRPC_CLOSURE_INIT(&do_nothing_cb, do_nothing, nullptr, |
|
|
|
|
grpc_schedule_on_exec_ctx); |
|
|
|
|
gpr_mu_lock(args->mu); |
|
|
|
|
grpc_pollset_shutdown(args->pollset, &do_nothing_cb); |
|
|
|
|
gpr_mu_unlock(args->mu); |
|
|
|
|
// exec_ctx needs to be flushed before calling grpc_pollset_destroy()
|
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
grpc_pollset_destroy(args->pollset); |
|
|
|
|
gpr_free(args->pollset); |
|
|
|
|
} |
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
static grpc_millis n_sec_deadline(int seconds) { |
|
|
|
|
grpc_millis NSecDeadline(int seconds) { |
|
|
|
|
return grpc_timespec_to_millis_round_up( |
|
|
|
|
grpc_timeout_seconds_to_deadline(seconds)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void poll_pollset_until_request_done(args_struct* args) { |
|
|
|
|
// Try to give enough time for c-ares to run through its retries
|
|
|
|
|
// a few times if needed.
|
|
|
|
|
grpc_millis deadline = n_sec_deadline(90); |
|
|
|
|
while (true) { |
|
|
|
|
const char* g_resolver_type = ""; |
|
|
|
|
|
|
|
|
|
class ResolveAddressTest : public ::testing::Test { |
|
|
|
|
public: |
|
|
|
|
ResolveAddressTest() { |
|
|
|
|
grpc_init(); |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
pollset_ = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size())); |
|
|
|
|
grpc_pollset_init(pollset_, &mu_); |
|
|
|
|
pollset_set_ = grpc_pollset_set_create(); |
|
|
|
|
grpc_pollset_set_add_pollset(pollset_set_, pollset_); |
|
|
|
|
default_inject_config_ = grpc_ares_test_only_inject_config; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
~ResolveAddressTest() override { |
|
|
|
|
{ |
|
|
|
|
grpc_core::MutexLockForGprMu lock(args->mu); |
|
|
|
|
if (args->done) { |
|
|
|
|
break; |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
grpc_pollset_set_del_pollset(pollset_set_, pollset_); |
|
|
|
|
grpc_pollset_set_destroy(pollset_set_); |
|
|
|
|
grpc_closure do_nothing_cb; |
|
|
|
|
GRPC_CLOSURE_INIT(&do_nothing_cb, DoNothing, nullptr, |
|
|
|
|
grpc_schedule_on_exec_ctx); |
|
|
|
|
gpr_mu_lock(mu_); |
|
|
|
|
grpc_pollset_shutdown(pollset_, &do_nothing_cb); |
|
|
|
|
gpr_mu_unlock(mu_); |
|
|
|
|
// exec_ctx needs to be flushed before calling grpc_pollset_destroy()
|
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
grpc_pollset_destroy(pollset_); |
|
|
|
|
gpr_free(pollset_); |
|
|
|
|
// reset this since it might have been altered
|
|
|
|
|
grpc_ares_test_only_inject_config = default_inject_config_; |
|
|
|
|
} |
|
|
|
|
grpc_shutdown(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void PollPollsetUntilRequestDone() { |
|
|
|
|
// Try to give enough time for c-ares to run through its retries
|
|
|
|
|
// a few times if needed.
|
|
|
|
|
grpc_millis deadline = NSecDeadline(90); |
|
|
|
|
while (true) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
{ |
|
|
|
|
grpc_core::MutexLockForGprMu lock(mu_); |
|
|
|
|
if (done_) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
grpc_millis time_left = deadline - grpc_core::ExecCtx::Get()->Now(); |
|
|
|
|
gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64, done_, time_left); |
|
|
|
|
ASSERT_GE(time_left, 0); |
|
|
|
|
grpc_pollset_worker* worker = nullptr; |
|
|
|
|
GRPC_LOG_IF_ERROR("pollset_work", grpc_pollset_work(pollset_, &worker, |
|
|
|
|
NSecDeadline(1))); |
|
|
|
|
} |
|
|
|
|
grpc_millis time_left = deadline - grpc_core::ExecCtx::Get()->Now(); |
|
|
|
|
gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64, args->done, time_left); |
|
|
|
|
GPR_ASSERT(time_left >= 0); |
|
|
|
|
grpc_pollset_worker* worker = nullptr; |
|
|
|
|
GRPC_LOG_IF_ERROR( |
|
|
|
|
"pollset_work", |
|
|
|
|
grpc_pollset_work(args->pollset, &worker, n_sec_deadline(1))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
gpr_event_set(&args->ev, reinterpret_cast<void*>(1)); |
|
|
|
|
|
|
|
|
|
void MustSucceed(absl::StatusOr<std::vector<grpc_resolved_address>> result) { |
|
|
|
|
EXPECT_EQ(result.status(), absl::OkStatus()); |
|
|
|
|
EXPECT_FALSE(result->empty()); |
|
|
|
|
Finish(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void MustFail(absl::StatusOr<std::vector<grpc_resolved_address>> result) { |
|
|
|
|
EXPECT_NE(result.status(), absl::OkStatus()); |
|
|
|
|
Finish(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void MustFailExpectCancelledErrorMessage( |
|
|
|
|
absl::StatusOr<std::vector<grpc_resolved_address>> result) { |
|
|
|
|
EXPECT_NE(result.status(), absl::OkStatus()); |
|
|
|
|
EXPECT_THAT(result.status().ToString(), |
|
|
|
|
testing::HasSubstr("DNS query cancelled")); |
|
|
|
|
Finish(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DontCare( |
|
|
|
|
absl::StatusOr<std::vector<grpc_resolved_address>> /* result */) { |
|
|
|
|
Finish(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// This test assumes the environment has an ipv6 loopback
|
|
|
|
|
void MustSucceedWithIPv6First( |
|
|
|
|
absl::StatusOr<std::vector<grpc_resolved_address>> result) { |
|
|
|
|
EXPECT_EQ(result.status(), absl::OkStatus()); |
|
|
|
|
EXPECT_TRUE(!result->empty() && |
|
|
|
|
reinterpret_cast<const struct sockaddr*>((*result)[0].addr) |
|
|
|
|
->sa_family == AF_INET6); |
|
|
|
|
Finish(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void MustSucceedWithIPv4First( |
|
|
|
|
absl::StatusOr<std::vector<grpc_resolved_address>> result) { |
|
|
|
|
EXPECT_EQ(result.status(), absl::OkStatus()); |
|
|
|
|
EXPECT_TRUE(!result->empty() && |
|
|
|
|
reinterpret_cast<const struct sockaddr*>((*result)[0].addr) |
|
|
|
|
->sa_family == AF_INET); |
|
|
|
|
Finish(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_pollset_set* pollset_set() const { return pollset_set_; } |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
static void DoNothing(void* /*arg*/, grpc_error_handle /*error*/) {} |
|
|
|
|
|
|
|
|
|
void Finish() { |
|
|
|
|
grpc_core::MutexLockForGprMu lock(mu_); |
|
|
|
|
done_ = true; |
|
|
|
|
GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(pollset_, nullptr)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gpr_mu* mu_; |
|
|
|
|
bool done_ = false; // guarded by mu
|
|
|
|
|
grpc_pollset* pollset_; // guarded by mu
|
|
|
|
|
grpc_pollset_set* pollset_set_; |
|
|
|
|
// the default value of grpc_ares_test_only_inject_config, which might
|
|
|
|
|
// be modified during a test
|
|
|
|
|
void (*default_inject_config_)(ares_channel channel) = nullptr; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
TEST_F(ResolveAddressTest, Localhost) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
"localhost:1", "", pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::MustSucceed, this)); |
|
|
|
|
r->Start(); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void must_succeed(void* argsp, grpc_error_handle err) { |
|
|
|
|
args_struct* args = static_cast<args_struct*>(argsp); |
|
|
|
|
GPR_ASSERT(err == GRPC_ERROR_NONE); |
|
|
|
|
GPR_ASSERT(args->addrs != nullptr); |
|
|
|
|
GPR_ASSERT(args->addrs->naddrs > 0); |
|
|
|
|
grpc_core::MutexLockForGprMu lock(args->mu); |
|
|
|
|
args->done = true; |
|
|
|
|
GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, nullptr)); |
|
|
|
|
TEST_F(ResolveAddressTest, DefaultPort) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
"localhost", "1", pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::MustSucceed, this)); |
|
|
|
|
r->Start(); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void must_fail(void* argsp, grpc_error_handle err) { |
|
|
|
|
args_struct* args = static_cast<args_struct*>(argsp); |
|
|
|
|
GPR_ASSERT(err != GRPC_ERROR_NONE); |
|
|
|
|
grpc_core::MutexLockForGprMu lock(args->mu); |
|
|
|
|
args->done = true; |
|
|
|
|
GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, nullptr)); |
|
|
|
|
TEST_F(ResolveAddressTest, LocalhostResultHasIPv6First) { |
|
|
|
|
if (std::string(g_resolver_type) != "ares") { |
|
|
|
|
GTEST_SKIP() << "this test is only valid with the c-ares resolver"; |
|
|
|
|
} |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
"localhost:1", "", pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::MustSucceedWithIPv6First, this)); |
|
|
|
|
r->Start(); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// This test assumes the environment has an ipv6 loopback
|
|
|
|
|
static void must_succeed_with_ipv6_first(void* argsp, grpc_error_handle err) { |
|
|
|
|
args_struct* args = static_cast<args_struct*>(argsp); |
|
|
|
|
GPR_ASSERT(err == GRPC_ERROR_NONE); |
|
|
|
|
GPR_ASSERT(args->addrs != nullptr); |
|
|
|
|
GPR_ASSERT(args->addrs->naddrs > 0); |
|
|
|
|
const struct sockaddr* first_address = |
|
|
|
|
reinterpret_cast<const struct sockaddr*>(args->addrs->addrs[0].addr); |
|
|
|
|
GPR_ASSERT(first_address->sa_family == AF_INET6); |
|
|
|
|
grpc_core::MutexLockForGprMu lock(args->mu); |
|
|
|
|
args->done = true; |
|
|
|
|
GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, nullptr)); |
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
bool IPv6DisabledGetSourceAddr(address_sorting_source_addr_factory* /*factory*/, |
|
|
|
|
const address_sorting_address* dest_addr, |
|
|
|
|
address_sorting_address* source_addr) { |
|
|
|
|
// Mock lack of IPv6. For IPv4, set the source addr to be the same
|
|
|
|
|
// as the destination; tests won't actually connect on the result anyways.
|
|
|
|
|
if (address_sorting_abstract_get_family(dest_addr) == |
|
|
|
|
ADDRESS_SORTING_AF_INET6) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
memcpy(source_addr->addr, &dest_addr->addr, dest_addr->len); |
|
|
|
|
source_addr->len = dest_addr->len; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void must_succeed_with_ipv4_first(void* argsp, grpc_error_handle err) { |
|
|
|
|
args_struct* args = static_cast<args_struct*>(argsp); |
|
|
|
|
GPR_ASSERT(err == GRPC_ERROR_NONE); |
|
|
|
|
GPR_ASSERT(args->addrs != nullptr); |
|
|
|
|
GPR_ASSERT(args->addrs->naddrs > 0); |
|
|
|
|
const struct sockaddr* first_address = |
|
|
|
|
reinterpret_cast<const struct sockaddr*>(args->addrs->addrs[0].addr); |
|
|
|
|
GPR_ASSERT(first_address->sa_family == AF_INET); |
|
|
|
|
grpc_core::MutexLockForGprMu lock(args->mu); |
|
|
|
|
args->done = true; |
|
|
|
|
GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, nullptr)); |
|
|
|
|
void DeleteSourceAddrFactory(address_sorting_source_addr_factory* factory) { |
|
|
|
|
delete factory; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_localhost(void) { |
|
|
|
|
const address_sorting_source_addr_factory_vtable |
|
|
|
|
kMockIpv6DisabledSourceAddrFactoryVtable = { |
|
|
|
|
IPv6DisabledGetSourceAddr, |
|
|
|
|
DeleteSourceAddrFactory, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
TEST_F(ResolveAddressTest, LocalhostResultHasIPv4FirstWhenIPv6IsntAvalailable) { |
|
|
|
|
if (std::string(g_resolver_type) != "ares") { |
|
|
|
|
GTEST_SKIP() << "this test is only valid with the c-ares resolver"; |
|
|
|
|
} |
|
|
|
|
// Mock the kernel source address selection. Note that source addr factory
|
|
|
|
|
// is reset to its default value during grpc initialization for each test.
|
|
|
|
|
address_sorting_source_addr_factory* mock = |
|
|
|
|
new address_sorting_source_addr_factory(); |
|
|
|
|
mock->vtable = &kMockIpv6DisabledSourceAddrFactoryVtable; |
|
|
|
|
address_sorting_override_source_addr_factory_for_testing(mock); |
|
|
|
|
// run the test
|
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
args_struct args; |
|
|
|
|
args_init(&args); |
|
|
|
|
grpc_resolve_address( |
|
|
|
|
"localhost:1", nullptr, args.pollset_set, |
|
|
|
|
GRPC_CLOSURE_CREATE(must_succeed, &args, grpc_schedule_on_exec_ctx), |
|
|
|
|
&args.addrs); |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
"localhost:1", "", pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::MustSucceedWithIPv4First, this)); |
|
|
|
|
r->Start(); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
poll_pollset_until_request_done(&args); |
|
|
|
|
args_finish(&args); |
|
|
|
|
PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_default_port(void) { |
|
|
|
|
TEST_F(ResolveAddressTest, NonNumericDefaultPort) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
args_struct args; |
|
|
|
|
args_init(&args); |
|
|
|
|
grpc_resolve_address( |
|
|
|
|
"localhost", "1", args.pollset_set, |
|
|
|
|
GRPC_CLOSURE_CREATE(must_succeed, &args, grpc_schedule_on_exec_ctx), |
|
|
|
|
&args.addrs); |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
"localhost", "http", pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::MustSucceed, this)); |
|
|
|
|
r->Start(); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
poll_pollset_until_request_done(&args); |
|
|
|
|
args_finish(&args); |
|
|
|
|
PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_localhost_result_has_ipv6_first(void) { |
|
|
|
|
TEST_F(ResolveAddressTest, MissingDefaultPort) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
args_struct args; |
|
|
|
|
args_init(&args); |
|
|
|
|
grpc_resolve_address("localhost:1", nullptr, args.pollset_set, |
|
|
|
|
GRPC_CLOSURE_CREATE(must_succeed_with_ipv6_first, &args, |
|
|
|
|
grpc_schedule_on_exec_ctx), |
|
|
|
|
&args.addrs); |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
"localhost", "", pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::MustFail, this)); |
|
|
|
|
r->Start(); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
poll_pollset_until_request_done(&args); |
|
|
|
|
args_finish(&args); |
|
|
|
|
PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_localhost_result_has_ipv4_first_when_ipv6_isnt_available( |
|
|
|
|
void) { |
|
|
|
|
TEST_F(ResolveAddressTest, IPv6WithPort) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
args_struct args; |
|
|
|
|
args_init(&args); |
|
|
|
|
grpc_resolve_address("localhost:1", nullptr, args.pollset_set, |
|
|
|
|
GRPC_CLOSURE_CREATE(must_succeed_with_ipv4_first, &args, |
|
|
|
|
grpc_schedule_on_exec_ctx), |
|
|
|
|
&args.addrs); |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
"[2001:db8::1]:1", "", pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::MustSucceed, this)); |
|
|
|
|
r->Start(); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
poll_pollset_until_request_done(&args); |
|
|
|
|
args_finish(&args); |
|
|
|
|
PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_non_numeric_default_port(void) { |
|
|
|
|
void TestIPv6WithoutPort(ResolveAddressTest* test, const char* target) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
args_struct args; |
|
|
|
|
args_init(&args); |
|
|
|
|
grpc_resolve_address( |
|
|
|
|
"localhost", "https", args.pollset_set, |
|
|
|
|
GRPC_CLOSURE_CREATE(must_succeed, &args, grpc_schedule_on_exec_ctx), |
|
|
|
|
&args.addrs); |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
target, "80", test->pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::MustSucceed, test)); |
|
|
|
|
r->Start(); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
poll_pollset_until_request_done(&args); |
|
|
|
|
args_finish(&args); |
|
|
|
|
test->PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST_F(ResolveAddressTest, IPv6WithoutPortNoBrackets) { |
|
|
|
|
TestIPv6WithoutPort(this, "2001:db8::1"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_missing_default_port(void) { |
|
|
|
|
TEST_F(ResolveAddressTest, IPv6WithoutPortWithBrackets) { |
|
|
|
|
TestIPv6WithoutPort(this, "[2001:db8::1]"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST_F(ResolveAddressTest, IPv6WithoutPortV4MappedV6) { |
|
|
|
|
TestIPv6WithoutPort(this, "2001:db8::1.2.3.4"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TestInvalidIPAddress(ResolveAddressTest* test, const char* target) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
args_struct args; |
|
|
|
|
args_init(&args); |
|
|
|
|
grpc_resolve_address( |
|
|
|
|
"localhost", nullptr, args.pollset_set, |
|
|
|
|
GRPC_CLOSURE_CREATE(must_fail, &args, grpc_schedule_on_exec_ctx), |
|
|
|
|
&args.addrs); |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
target, "", test->pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::MustFail, test)); |
|
|
|
|
r->Start(); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
poll_pollset_until_request_done(&args); |
|
|
|
|
args_finish(&args); |
|
|
|
|
test->PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_ipv6_with_port(void) { |
|
|
|
|
TEST_F(ResolveAddressTest, InvalidIPv4Addresses) { |
|
|
|
|
TestInvalidIPAddress(this, "293.283.1238.3:1"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST_F(ResolveAddressTest, InvalidIPv6Addresses) { |
|
|
|
|
TestInvalidIPAddress(this, "[2001:db8::11111]:1"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TestUnparseableHostPort(ResolveAddressTest* test, const char* target) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
args_struct args; |
|
|
|
|
args_init(&args); |
|
|
|
|
grpc_resolve_address( |
|
|
|
|
"[2001:db8::1]:1", nullptr, args.pollset_set, |
|
|
|
|
GRPC_CLOSURE_CREATE(must_succeed, &args, grpc_schedule_on_exec_ctx), |
|
|
|
|
&args.addrs); |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
target, "1", test->pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::MustFail, test)); |
|
|
|
|
r->Start(); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
poll_pollset_until_request_done(&args); |
|
|
|
|
args_finish(&args); |
|
|
|
|
test->PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_ipv6_without_port(void) { |
|
|
|
|
const char* const kCases[] = { |
|
|
|
|
"2001:db8::1", |
|
|
|
|
"2001:db8::1.2.3.4", |
|
|
|
|
"[2001:db8::1]", |
|
|
|
|
}; |
|
|
|
|
unsigned i; |
|
|
|
|
for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
args_struct args; |
|
|
|
|
args_init(&args); |
|
|
|
|
grpc_resolve_address( |
|
|
|
|
kCases[i], "80", args.pollset_set, |
|
|
|
|
GRPC_CLOSURE_CREATE(must_succeed, &args, grpc_schedule_on_exec_ctx), |
|
|
|
|
&args.addrs); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
poll_pollset_until_request_done(&args); |
|
|
|
|
args_finish(&args); |
|
|
|
|
} |
|
|
|
|
TEST_F(ResolveAddressTest, UnparseableHostPortsOnlyBracket) { |
|
|
|
|
TestUnparseableHostPort(this, "["); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_invalid_ip_addresses(void) { |
|
|
|
|
const char* const kCases[] = { |
|
|
|
|
"293.283.1238.3:1", |
|
|
|
|
"[2001:db8::11111]:1", |
|
|
|
|
}; |
|
|
|
|
unsigned i; |
|
|
|
|
for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
args_struct args; |
|
|
|
|
args_init(&args); |
|
|
|
|
grpc_resolve_address( |
|
|
|
|
kCases[i], nullptr, args.pollset_set, |
|
|
|
|
GRPC_CLOSURE_CREATE(must_fail, &args, grpc_schedule_on_exec_ctx), |
|
|
|
|
&args.addrs); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
poll_pollset_until_request_done(&args); |
|
|
|
|
args_finish(&args); |
|
|
|
|
} |
|
|
|
|
TEST_F(ResolveAddressTest, UnparseableHostPortsMissingRightBracket) { |
|
|
|
|
TestUnparseableHostPort(this, "[::1"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_unparseable_hostports(void) { |
|
|
|
|
const char* const kCases[] = { |
|
|
|
|
"[", "[::1", "[::1]bad", "[1.2.3.4]", "[localhost]", "[localhost]:1", |
|
|
|
|
}; |
|
|
|
|
unsigned i; |
|
|
|
|
for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
args_struct args; |
|
|
|
|
args_init(&args); |
|
|
|
|
grpc_resolve_address( |
|
|
|
|
kCases[i], "1", args.pollset_set, |
|
|
|
|
GRPC_CLOSURE_CREATE(must_fail, &args, grpc_schedule_on_exec_ctx), |
|
|
|
|
&args.addrs); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
poll_pollset_until_request_done(&args); |
|
|
|
|
args_finish(&args); |
|
|
|
|
} |
|
|
|
|
TEST_F(ResolveAddressTest, UnparseableHostPortsBadPort) { |
|
|
|
|
TestUnparseableHostPort(this, "[::1]bad"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
typedef struct mock_ipv6_disabled_source_addr_factory { |
|
|
|
|
address_sorting_source_addr_factory base; |
|
|
|
|
} mock_ipv6_disabled_source_addr_factory; |
|
|
|
|
TEST_F(ResolveAddressTest, UnparseableHostPortsBadIPv6) { |
|
|
|
|
TestUnparseableHostPort(this, "[1.2.3.4]"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool mock_ipv6_disabled_source_addr_factory_get_source_addr( |
|
|
|
|
address_sorting_source_addr_factory* /*factory*/, |
|
|
|
|
const address_sorting_address* dest_addr, |
|
|
|
|
address_sorting_address* source_addr) { |
|
|
|
|
// Mock lack of IPv6. For IPv4, set the source addr to be the same
|
|
|
|
|
// as the destination; tests won't actually connect on the result anyways.
|
|
|
|
|
if (address_sorting_abstract_get_family(dest_addr) == |
|
|
|
|
ADDRESS_SORTING_AF_INET6) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
memcpy(source_addr->addr, &dest_addr->addr, dest_addr->len); |
|
|
|
|
source_addr->len = dest_addr->len; |
|
|
|
|
return true; |
|
|
|
|
TEST_F(ResolveAddressTest, UnparseableHostPortsBadLocalhost) { |
|
|
|
|
TestUnparseableHostPort(this, "[localhost]"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void mock_ipv6_disabled_source_addr_factory_destroy( |
|
|
|
|
address_sorting_source_addr_factory* factory) { |
|
|
|
|
mock_ipv6_disabled_source_addr_factory* f = |
|
|
|
|
reinterpret_cast<mock_ipv6_disabled_source_addr_factory*>(factory); |
|
|
|
|
gpr_free(f); |
|
|
|
|
TEST_F(ResolveAddressTest, UnparseableHostPortsBadLocalhostWithPort) { |
|
|
|
|
TestUnparseableHostPort(this, "[localhost]:1"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const address_sorting_source_addr_factory_vtable |
|
|
|
|
kMockIpv6DisabledSourceAddrFactoryVtable = { |
|
|
|
|
mock_ipv6_disabled_source_addr_factory_get_source_addr, |
|
|
|
|
mock_ipv6_disabled_source_addr_factory_destroy, |
|
|
|
|
}; |
|
|
|
|
// Kick off a simple DNS resolution and then immediately cancel. This
|
|
|
|
|
// test doesn't care what the result is, just that we don't crash etc.
|
|
|
|
|
TEST_F(ResolveAddressTest, ImmediateCancel) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
"localhost:1", "1", pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::DontCare, this)); |
|
|
|
|
r->Start(); |
|
|
|
|
r.reset(); // cancel the resolution
|
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); |
|
|
|
|
PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) { |
|
|
|
|
// First set the resolver type based off of --resolver
|
|
|
|
|
const char* resolver_type = nullptr; |
|
|
|
|
gpr_cmdline* cl = gpr_cmdline_create("resolve address test"); |
|
|
|
|
gpr_cmdline_add_string(cl, "resolver", "Resolver type (ares or native)", |
|
|
|
|
&resolver_type); |
|
|
|
|
// In case that there are more than one argument on the command line,
|
|
|
|
|
// --resolver will always be the first one, so only parse the first argument
|
|
|
|
|
// (other arguments may be unknown to cl)
|
|
|
|
|
gpr_cmdline_parse(cl, argc > 2 ? 2 : argc, argv); |
|
|
|
|
grpc_core::UniquePtr<char> resolver = |
|
|
|
|
GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver); |
|
|
|
|
if (strlen(resolver.get()) != 0) { |
|
|
|
|
gpr_log(GPR_INFO, "Warning: overriding resolver setting of %s", |
|
|
|
|
resolver.get()); |
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
int g_fake_non_responsive_dns_server_port; |
|
|
|
|
|
|
|
|
|
void InjectNonResponsiveDNSServer(ares_channel channel) { |
|
|
|
|
gpr_log(GPR_DEBUG, |
|
|
|
|
"Injecting broken nameserver list. Bad server address:|[::1]:%d|.", |
|
|
|
|
g_fake_non_responsive_dns_server_port); |
|
|
|
|
// Configure a non-responsive DNS server at the front of c-ares's nameserver
|
|
|
|
|
// list.
|
|
|
|
|
struct ares_addr_port_node dns_server_addrs[1]; |
|
|
|
|
memset(dns_server_addrs, 0, sizeof(dns_server_addrs)); |
|
|
|
|
dns_server_addrs[0].family = AF_INET6; |
|
|
|
|
(reinterpret_cast<char*>(&dns_server_addrs[0].addr.addr6))[15] = 0x1; |
|
|
|
|
dns_server_addrs[0].tcp_port = g_fake_non_responsive_dns_server_port; |
|
|
|
|
dns_server_addrs[0].udp_port = g_fake_non_responsive_dns_server_port; |
|
|
|
|
dns_server_addrs[0].next = nullptr; |
|
|
|
|
ASSERT_EQ(ares_set_servers_ports(channel, dns_server_addrs), ARES_SUCCESS); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
TEST_F(ResolveAddressTest, CancelWithNonResponsiveDNSServer) { |
|
|
|
|
if (std::string(g_resolver_type) != "ares") { |
|
|
|
|
GTEST_SKIP() << "the native resolver doesn't support cancellation, so we " |
|
|
|
|
"can only test this with c-ares"; |
|
|
|
|
} |
|
|
|
|
if (resolver_type != nullptr && gpr_stricmp(resolver_type, "native") == 0) { |
|
|
|
|
GPR_GLOBAL_CONFIG_SET(grpc_dns_resolver, "native"); |
|
|
|
|
} else if (resolver_type != nullptr && |
|
|
|
|
gpr_stricmp(resolver_type, "ares") == 0) { |
|
|
|
|
GPR_GLOBAL_CONFIG_SET(grpc_dns_resolver, "ares"); |
|
|
|
|
// Inject an unresponsive DNS server into the resolver's DNS server config
|
|
|
|
|
grpc_core::testing::FakeUdpAndTcpServer fake_dns_server( |
|
|
|
|
grpc_core::testing::FakeUdpAndTcpServer::AcceptMode:: |
|
|
|
|
kWaitForClientToSendFirstBytes, |
|
|
|
|
grpc_core::testing::FakeUdpAndTcpServer::CloseSocketUponCloseFromPeer); |
|
|
|
|
g_fake_non_responsive_dns_server_port = fake_dns_server.port(); |
|
|
|
|
grpc_ares_test_only_inject_config = InjectNonResponsiveDNSServer; |
|
|
|
|
// Run the test
|
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
auto r = grpc_core::GetDNSResolver()->ResolveName( |
|
|
|
|
"foo.bar.com:1", "1", pollset_set(), |
|
|
|
|
absl::bind_front(&ResolveAddressTest::MustFailExpectCancelledErrorMessage, |
|
|
|
|
this)); |
|
|
|
|
r->Start(); |
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); // initiate DNS requests
|
|
|
|
|
r.reset(); // cancel the resolution
|
|
|
|
|
grpc_core::ExecCtx::Get()->Flush(); // let cancellation work finish
|
|
|
|
|
PollPollsetUntilRequestDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) { |
|
|
|
|
// Configure the DNS resolver (c-ares vs. native) based on the
|
|
|
|
|
// name of the binary. TODO(apolcyn): is there a way to pass command
|
|
|
|
|
// line flags to a gtest that it works in all of our test environments?
|
|
|
|
|
if (absl::StrContains(std::string(argv[0]), "using_native_resolver")) { |
|
|
|
|
g_resolver_type = "native"; |
|
|
|
|
} else if (absl::StrContains(std::string(argv[0]), "using_ares_resolver")) { |
|
|
|
|
g_resolver_type = "ares"; |
|
|
|
|
} else { |
|
|
|
|
gpr_log(GPR_ERROR, "--resolver_type was not set to ares or native"); |
|
|
|
|
abort(); |
|
|
|
|
GPR_ASSERT(0); |
|
|
|
|
} |
|
|
|
|
// Run the test.
|
|
|
|
|
GPR_GLOBAL_CONFIG_SET(grpc_dns_resolver, g_resolver_type); |
|
|
|
|
::testing::InitGoogleTest(&argc, argv); |
|
|
|
|
grpc::testing::TestEnvironment env(argc, argv); |
|
|
|
|
grpc_init(); |
|
|
|
|
{ |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
test_localhost(); |
|
|
|
|
test_default_port(); |
|
|
|
|
test_non_numeric_default_port(); |
|
|
|
|
test_missing_default_port(); |
|
|
|
|
test_ipv6_with_port(); |
|
|
|
|
test_ipv6_without_port(); |
|
|
|
|
test_invalid_ip_addresses(); |
|
|
|
|
test_unparseable_hostports(); |
|
|
|
|
if (gpr_stricmp(resolver_type, "ares") == 0) { |
|
|
|
|
// This behavior expectation is specific to c-ares.
|
|
|
|
|
test_localhost_result_has_ipv6_first(); |
|
|
|
|
} |
|
|
|
|
grpc_core::Executor::ShutdownAll(); |
|
|
|
|
} |
|
|
|
|
gpr_cmdline_destroy(cl); |
|
|
|
|
grpc_shutdown(); |
|
|
|
|
// The following test uses
|
|
|
|
|
// "address_sorting_override_source_addr_factory_for_testing", which works
|
|
|
|
|
// on a per-grpc-init basis, and so it's simplest to run this next test
|
|
|
|
|
// within a standalone grpc_init/grpc_shutdown pair.
|
|
|
|
|
if (gpr_stricmp(resolver_type, "ares") == 0) { |
|
|
|
|
// Run a test case in which c-ares's address sorter
|
|
|
|
|
// thinks that IPv4 is available and IPv6 isn't.
|
|
|
|
|
grpc_init(); |
|
|
|
|
mock_ipv6_disabled_source_addr_factory* factory = |
|
|
|
|
static_cast<mock_ipv6_disabled_source_addr_factory*>( |
|
|
|
|
gpr_malloc(sizeof(mock_ipv6_disabled_source_addr_factory))); |
|
|
|
|
factory->base.vtable = &kMockIpv6DisabledSourceAddrFactoryVtable; |
|
|
|
|
address_sorting_override_source_addr_factory_for_testing(&factory->base); |
|
|
|
|
test_localhost_result_has_ipv4_first_when_ipv6_isnt_available(); |
|
|
|
|
grpc_shutdown(); |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
const auto result = RUN_ALL_TESTS(); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|