|  | @@ -23,6 +23,8 @@
 | 
	
		
			
				|  |  |  #include <grpc/support/sync.h>
 | 
	
		
			
				|  |  |  #include <grpc/support/time.h>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#include <address_sorting/address_sorting.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #include <string.h>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include "src/core/lib/gpr/env.h"
 | 
	
	
		
			
				|  | @@ -120,6 +122,35 @@ static void must_fail(void* argsp, grpc_error* err) {
 | 
	
		
			
				|  |  |    gpr_mu_unlock(args->mu);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// This test assumes the environment has an ipv6 loopback
 | 
	
		
			
				|  |  | +static void must_succeed_with_ipv6_first(void* argsp, grpc_error* 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);
 | 
	
		
			
				|  |  | +  gpr_atm_rel_store(&args->done_atm, 1);
 | 
	
		
			
				|  |  | +  gpr_mu_lock(args->mu);
 | 
	
		
			
				|  |  | +  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, nullptr));
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(args->mu);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void must_succeed_with_ipv4_first(void* argsp, grpc_error* 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);
 | 
	
		
			
				|  |  | +  gpr_atm_rel_store(&args->done_atm, 1);
 | 
	
		
			
				|  |  | +  gpr_mu_lock(args->mu);
 | 
	
		
			
				|  |  | +  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, nullptr));
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(args->mu);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static void test_localhost(void) {
 | 
	
		
			
				|  |  |    grpc_core::ExecCtx exec_ctx;
 | 
	
		
			
				|  |  |    args_struct args;
 | 
	
	
		
			
				|  | @@ -146,6 +177,33 @@ static void test_default_port(void) {
 | 
	
		
			
				|  |  |    args_finish(&args);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void test_localhost_result_has_ipv6_first(void) {
 | 
	
		
			
				|  |  | +  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);
 | 
	
		
			
				|  |  | +  grpc_core::ExecCtx::Get()->Flush();
 | 
	
		
			
				|  |  | +  poll_pollset_until_request_done(&args);
 | 
	
		
			
				|  |  | +  args_finish(&args);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void test_localhost_result_has_ipv4_first_when_ipv6_isnt_available(
 | 
	
		
			
				|  |  | +    void) {
 | 
	
		
			
				|  |  | +  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);
 | 
	
		
			
				|  |  | +  grpc_core::ExecCtx::Get()->Flush();
 | 
	
		
			
				|  |  | +  poll_pollset_until_request_done(&args);
 | 
	
		
			
				|  |  | +  args_finish(&args);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static void test_non_numeric_default_port(void) {
 | 
	
		
			
				|  |  |    grpc_core::ExecCtx exec_ctx;
 | 
	
		
			
				|  |  |    args_struct args;
 | 
	
	
		
			
				|  | @@ -245,6 +303,34 @@ static void test_unparseable_hostports(void) {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +typedef struct mock_ipv6_disabled_source_addr_factory {
 | 
	
		
			
				|  |  | +  address_sorting_source_addr_factory base;
 | 
	
		
			
				|  |  | +} mock_ipv6_disabled_source_addr_factory;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +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;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void mock_ipv6_disabled_source_addr_factory_destroy(
 | 
	
		
			
				|  |  | +    address_sorting_source_addr_factory* factory) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const address_sorting_source_addr_factory_vtable
 | 
	
		
			
				|  |  | +    kMockIpv6DisabledSourceAddrFactoryVtable = {
 | 
	
		
			
				|  |  | +        mock_ipv6_disabled_source_addr_factory_get_source_addr,
 | 
	
		
			
				|  |  | +        mock_ipv6_disabled_source_addr_factory_destroy,
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  int main(int argc, char** argv) {
 | 
	
		
			
				|  |  |    // First set the resolver type based off of --resolver
 | 
	
		
			
				|  |  |    const char* resolver_type = nullptr;
 | 
	
	
		
			
				|  | @@ -289,11 +375,26 @@ int main(int argc, char** argv) {
 | 
	
		
			
				|  |  |        // these unit tests under c-ares risks flakiness.
 | 
	
		
			
				|  |  |        test_invalid_ip_addresses();
 | 
	
		
			
				|  |  |        test_unparseable_hostports();
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      test_localhost_result_has_ipv6_first();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      grpc_executor_shutdown();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    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;
 | 
	
		
			
				|  |  | +    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;
 | 
	
		
			
				|  |  |  }
 |