diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c index ffaeeed3242c492d31a56358ab8d742e3ff0e73c..ca7c7315b5891a3cdc260c69b739e826d24db3c8 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c @@ -95,7 +95,7 @@ typedef struct { gpr_backoff backoff_state; /** currently resolving addresses */ - grpc_resolved_addresses *addresses; + grpc_lb_addresses *lb_addresses; } ares_dns_resolver; static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); @@ -158,19 +158,10 @@ static void dns_ares_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg, grpc_channel_args *result = NULL; GPR_ASSERT(r->resolving); r->resolving = false; - if (r->addresses != NULL) { - grpc_lb_addresses *addresses = grpc_lb_addresses_create( - r->addresses->naddrs, NULL /* user_data_vtable */); - for (size_t i = 0; i < r->addresses->naddrs; ++i) { - grpc_lb_addresses_set_address( - addresses, i, &r->addresses->addrs[i].addr, - r->addresses->addrs[i].len, false /* is_balancer */, - NULL /* balancer_name */, NULL /* user_data */); - } - grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(addresses); + if (r->lb_addresses != NULL) { + grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(r->lb_addresses); result = grpc_channel_args_copy_and_add(r->channel_args, &new_arg, 1); - grpc_resolved_addresses_destroy(r->addresses); - grpc_lb_addresses_destroy(exec_ctx, addresses); + grpc_lb_addresses_destroy(exec_ctx, r->lb_addresses); } else { gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now); @@ -220,10 +211,10 @@ static void dns_ares_start_resolving_locked(grpc_exec_ctx *exec_ctx, GRPC_RESOLVER_REF(&r->base, "dns-resolving"); GPR_ASSERT(!r->resolving); r->resolving = true; - r->addresses = NULL; - grpc_resolve_address(exec_ctx, r->name_to_resolve, r->default_port, - r->interested_parties, &r->dns_ares_on_resolved_locked, - &r->addresses); + r->lb_addresses = NULL; + grpc_resolve_grpclb_address_ares( + exec_ctx, r->name_to_resolve, r->default_port, r->interested_parties, + &r->dns_ares_on_resolved_locked, &r->lb_addresses); } static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, @@ -233,6 +224,7 @@ static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, *r->target_result = r->resolved_result == NULL ? NULL : grpc_channel_args_copy(r->resolved_result); + gpr_log(GPR_DEBUG, "dns_ares_maybe_finish_next_locked"); grpc_closure_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE); r->next_completion = NULL; r->published_version = r->resolved_version; @@ -255,14 +247,14 @@ static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { static grpc_resolver *dns_ares_create(grpc_exec_ctx *exec_ctx, grpc_resolver_args *args, const char *default_port) { - // Get name from args. + /* Get name from args. */ const char *path = args->uri->path; if (0 != strcmp(args->uri->authority, "")) { gpr_log(GPR_ERROR, "authority based dns uri's not supported"); return NULL; } if (path[0] == '/') ++path; - // Create resolver. + /* Create resolver. */ ares_dns_resolver *r = gpr_zalloc(sizeof(ares_dns_resolver)); grpc_resolver_init(&r->base, &dns_ares_resolver_vtable, args->combiner); r->name_to_resolve = gpr_strdup(path); diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c index 09c46a66e0b0fd981ab206f855bf1275f6d122a3..55059018c8e8fec98ee56c1dc281d9b76e127010 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c @@ -48,6 +48,7 @@ #include <grpc/support/string_util.h> #include <grpc/support/time.h> #include <grpc/support/useful.h> +#include <nameser.h> #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/iomgr_internal.h" @@ -59,16 +60,16 @@ static gpr_mu g_init_mu; typedef struct grpc_ares_request { /** following members are set in grpc_resolve_address_ares_impl */ - /** host to resolve, parsed from the name to resolve */ - char *host; - /** port to fill in sockaddr_in, parsed from the name to resolve */ - char *port; - /** default port to use */ - char *default_port; /** closure to call when the request completes */ grpc_closure *on_done; /** the pointer to receive the resolved addresses */ - grpc_resolved_addresses **addrs_out; + union { + grpc_resolved_addresses **addrs; + grpc_lb_addresses **lb_addrs; + } addrs_out; + /** if true, the output addresses are in the format of grpc_lb_addresses, + otherwise they are in the format of grpc_resolved_addresses */ + bool lb_addrs_out; /** the evernt driver used by this request */ grpc_ares_ev_driver *ev_driver; /** number of ongoing queries */ @@ -82,6 +83,18 @@ typedef struct grpc_ares_request { grpc_error *error; } grpc_ares_request; +typedef struct grpc_ares_hostbyname_request { + /** following members are set in create_hostbyname_request */ + /** the top-level request instance */ + grpc_ares_request *parent_request; + /** host to resolve, parsed from the name to resolve */ + char *host; + /** port to fill in sockaddr_in, parsed from the name to resolve */ + uint16_t port; + /** is it a grpclb address */ + bool is_balancer; +} grpc_ares_hostbyname_request; + static void do_basic_init(void) { gpr_mu_init(&g_init_mu); } static uint16_t strhtons(const char *port) { @@ -93,6 +106,10 @@ static uint16_t strhtons(const char *port) { return htons((unsigned short)atoi(port)); } +static void grpc_ares_request_ref(grpc_ares_request *r) { + gpr_ref(&r->pending_queries); +} + static void grpc_ares_request_unref(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) { /* If there are no pending queries, invoke on_done callback and destroy the @@ -105,77 +122,200 @@ static void grpc_ares_request_unref(grpc_exec_ctx *exec_ctx, the newly created exec_ctx, since the caller has been warned not to acquire locks in on_done. ares_dns_resolver is using combiner to protect resources needed by on_done. */ + gpr_log(GPR_DEBUG, "grpc_ares_request_unref NULl"); grpc_exec_ctx new_exec_ctx = GRPC_EXEC_CTX_INIT; grpc_closure_sched(&new_exec_ctx, r->on_done, r->error); grpc_exec_ctx_finish(&new_exec_ctx); } else { + gpr_log(GPR_DEBUG, "grpc_ares_request_unref exec_ctx"); grpc_closure_sched(exec_ctx, r->on_done, r->error); } gpr_mu_destroy(&r->mu); grpc_ares_ev_driver_destroy(r->ev_driver); - gpr_free(r->host); - gpr_free(r->port); - gpr_free(r->default_port); gpr_free(r); } } -static void on_done_cb(void *arg, int status, int timeouts, - struct hostent *hostent) { - grpc_ares_request *r = (grpc_ares_request *)arg; +static grpc_ares_hostbyname_request *create_hostbyname_request( + grpc_ares_request *parent_request, char *host, uint16_t port, + bool is_balancer) { + grpc_ares_hostbyname_request *hr = + gpr_zalloc(sizeof(grpc_ares_hostbyname_request)); + hr->parent_request = parent_request; + hr->host = gpr_strdup(host); + hr->port = port; + hr->is_balancer = is_balancer; + grpc_ares_request_ref(parent_request); + return hr; +} + +static void destroy_hostbyname_request(grpc_exec_ctx *exec_ctx, + grpc_ares_hostbyname_request *hr) { + grpc_ares_request_unref(exec_ctx, hr->parent_request); + gpr_free(hr->host); + gpr_free(hr); +} + +static void on_hostbyname_done_cb(void *arg, int status, int timeouts, + struct hostent *hostent) { + grpc_ares_hostbyname_request *hr = (grpc_ares_hostbyname_request *)arg; + grpc_ares_request *r = hr->parent_request; gpr_mu_lock(&r->mu); if (status == ARES_SUCCESS) { GRPC_ERROR_UNREF(r->error); r->error = GRPC_ERROR_NONE; r->success = true; - grpc_resolved_addresses **addresses = r->addrs_out; - if (*addresses == NULL) { - *addresses = gpr_malloc(sizeof(grpc_resolved_addresses)); - (*addresses)->naddrs = 0; - (*addresses)->addrs = NULL; + if (r->lb_addrs_out) { + grpc_lb_addresses **lb_addresses = r->addrs_out.lb_addrs; + if (*lb_addresses == NULL) { + *lb_addresses = grpc_lb_addresses_create(0, NULL); + } + size_t prev_naddr = (*lb_addresses)->num_addresses; + size_t i; + for (i = 0; hostent->h_addr_list[i] != NULL; i++) { + } + (*lb_addresses)->num_addresses += i; + (*lb_addresses)->addresses = + gpr_realloc((*lb_addresses)->addresses, + sizeof(grpc_lb_address) * (*lb_addresses)->num_addresses); + for (i = prev_naddr; i < (*lb_addresses)->num_addresses; i++) { + memset(&(*lb_addresses)->addresses[i], 0, sizeof(grpc_lb_address)); + if (hostent->h_addrtype == AF_INET6) { + size_t addr_len = sizeof(struct sockaddr_in6); + struct sockaddr_in6 addr; + memcpy(&addr.sin6_addr, hostent->h_addr_list[i - prev_naddr], + sizeof(struct in6_addr)); + addr.sin6_family = (sa_family_t)hostent->h_addrtype; + addr.sin6_port = hr->port; + grpc_lb_addresses_set_address( + *lb_addresses, i, &addr, addr_len, + hr->is_balancer /* is_balancer */, + hr->is_balancer ? strdup(hr->host) : NULL /* balancer_name */, + NULL /* user_data */); + + char output[INET6_ADDRSTRLEN]; + ares_inet_ntop(AF_INET6, &addr.sin6_addr, output, INET6_ADDRSTRLEN); + gpr_log(GPR_DEBUG, + "c-ares resolver gets a AF_INET6 result: \n" + " addr: %s\n port: %d\n sin6_scope_id: %d\n", + output, ntohs(hr->port), addr.sin6_scope_id); + } else { /* hostent->h_addrtype == AF_INET6 */ + size_t addr_len = sizeof(struct sockaddr_in); + struct sockaddr_in addr; + memcpy(&addr.sin_addr, hostent->h_addr_list[i - prev_naddr], + sizeof(struct in_addr)); + addr.sin_family = (sa_family_t)hostent->h_addrtype; + addr.sin_port = hr->port; + grpc_lb_addresses_set_address( + *lb_addresses, i, &addr, addr_len, + hr->is_balancer /* is_balancer */, + hr->is_balancer ? strdup(hr->host) : NULL /* balancer_name */, + NULL /* user_data */); + + char output[INET_ADDRSTRLEN]; + ares_inet_ntop(AF_INET, &addr.sin_addr, output, INET_ADDRSTRLEN); + gpr_log(GPR_DEBUG, + "c-ares resolver gets a AF_INET result: \n" + " addr: %s\n port: %d\n", + output, ntohs(hr->port)); + } + } + } else { /* r->lb_addrs_out */ + grpc_resolved_addresses **addresses = r->addrs_out.addrs; + if (*addresses == NULL) { + *addresses = gpr_malloc(sizeof(grpc_resolved_addresses)); + (*addresses)->naddrs = 0; + (*addresses)->addrs = NULL; + } + size_t prev_naddr = (*addresses)->naddrs; + size_t i; + for (i = 0; hostent->h_addr_list[i] != NULL; i++) { + } + (*addresses)->naddrs += i; + (*addresses)->addrs = + gpr_realloc((*addresses)->addrs, + sizeof(grpc_resolved_address) * (*addresses)->naddrs); + for (i = prev_naddr; i < (*addresses)->naddrs; i++) { + memset(&(*addresses)->addrs[i], 0, sizeof(grpc_resolved_address)); + if (hostent->h_addrtype == AF_INET6) { + (*addresses)->addrs[i].len = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *addr = + (struct sockaddr_in6 *)&(*addresses)->addrs[i].addr; + addr->sin6_family = (sa_family_t)hostent->h_addrtype; + addr->sin6_port = hr->port; + + char output[INET6_ADDRSTRLEN]; + memcpy(&addr->sin6_addr, hostent->h_addr_list[i - prev_naddr], + sizeof(struct in6_addr)); + ares_inet_ntop(AF_INET6, &addr->sin6_addr, output, INET6_ADDRSTRLEN); + gpr_log(GPR_DEBUG, + "c-ares resolver gets a AF_INET6 result: \n" + " addr: %s\n port: %d\n sin6_scope_id: %d\n", + output, ntohs(hr->port), addr->sin6_scope_id); + } else { /* hostent->h_addrtype == AF_INET6 */ + (*addresses)->addrs[i].len = sizeof(struct sockaddr_in); + struct sockaddr_in *addr = + (struct sockaddr_in *)&(*addresses)->addrs[i].addr; + memcpy(&addr->sin_addr, hostent->h_addr_list[i - prev_naddr], + sizeof(struct in_addr)); + addr->sin_family = (sa_family_t)hostent->h_addrtype; + addr->sin_port = hr->port; + + char output[INET_ADDRSTRLEN]; + ares_inet_ntop(AF_INET, &addr->sin_addr, output, INET_ADDRSTRLEN); + gpr_log(GPR_DEBUG, + "c-ares resolver gets a AF_INET result: \n" + " addr: %s\n port: %d\n", + output, ntohs(hr->port)); + } + } } - size_t prev_naddr = (*addresses)->naddrs; - size_t i; - for (i = 0; hostent->h_addr_list[i] != NULL; i++) { + } else if (!r->success) { + char *error_msg; + gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s", + ares_strerror(status)); + grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + if (r->error == GRPC_ERROR_NONE) { + r->error = error; + } else { + r->error = grpc_error_add_child(error, r->error); } - (*addresses)->naddrs += i; - (*addresses)->addrs = - gpr_realloc((*addresses)->addrs, - sizeof(grpc_resolved_address) * (*addresses)->naddrs); - for (i = prev_naddr; i < (*addresses)->naddrs; i++) { - memset(&(*addresses)->addrs[i], 0, sizeof(grpc_resolved_address)); - if (hostent->h_addrtype == AF_INET6) { - (*addresses)->addrs[i].len = sizeof(struct sockaddr_in6); - struct sockaddr_in6 *addr = - (struct sockaddr_in6 *)&(*addresses)->addrs[i].addr; - addr->sin6_family = (sa_family_t)hostent->h_addrtype; - addr->sin6_port = strhtons(r->port); - - char output[INET6_ADDRSTRLEN]; - memcpy(&addr->sin6_addr, hostent->h_addr_list[i - prev_naddr], - sizeof(struct in6_addr)); - ares_inet_ntop(AF_INET6, &addr->sin6_addr, output, INET6_ADDRSTRLEN); - gpr_log(GPR_DEBUG, - "c-ares resolver gets a AF_INET6 result: \n" - " addr: %s\n port: %s\n sin6_scope_id: %d\n", - output, r->port, addr->sin6_scope_id); - } else { - (*addresses)->addrs[i].len = sizeof(struct sockaddr_in); - struct sockaddr_in *addr = - (struct sockaddr_in *)&(*addresses)->addrs[i].addr; - memcpy(&addr->sin_addr, hostent->h_addr_list[i - prev_naddr], - sizeof(struct in_addr)); - addr->sin_family = (sa_family_t)hostent->h_addrtype; - addr->sin_port = strhtons(r->port); - - char output[INET_ADDRSTRLEN]; - ares_inet_ntop(AF_INET, &addr->sin_addr, output, INET_ADDRSTRLEN); - gpr_log(GPR_DEBUG, - "c-ares resolver gets a AF_INET result: \n" - " addr: %s\n port: %s\n", - output, r->port); + } + gpr_mu_unlock(&r->mu); + destroy_hostbyname_request(NULL, hr); +} + +static void on_srv_query_done_cb(void *arg, int status, int timeouts, + unsigned char *abuf, int alen) { + grpc_ares_request *r = (grpc_ares_request *)arg; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + gpr_log(GPR_DEBUG, "on_query_srv_done_cb"); + if (status == ARES_SUCCESS) { + gpr_log(GPR_DEBUG, "on_query_srv_done_cb ARES_SUCCESS"); + struct ares_srv_reply *reply; + const int parse_status = ares_parse_srv_reply(abuf, alen, &reply); + if (parse_status == ARES_SUCCESS) { + ares_channel *channel = grpc_ares_ev_driver_get_channel(r->ev_driver); + for (struct ares_srv_reply *srv_it = reply; srv_it != NULL; + srv_it = srv_it->next) { + if (grpc_ipv6_loopback_available()) { + grpc_ares_hostbyname_request *hr = create_hostbyname_request( + r, srv_it->host, srv_it->port, true /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET6, + on_hostbyname_done_cb, hr); + } + grpc_ares_hostbyname_request *hr = create_hostbyname_request( + r, srv_it->host, srv_it->port, true /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET, on_hostbyname_done_cb, + hr); + grpc_ares_ev_driver_start(&exec_ctx, r->ev_driver); } } + + if (reply != NULL) { + ares_free_data(reply); + } } else if (!r->success) { char *error_msg; gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s", @@ -188,15 +328,15 @@ static void on_done_cb(void *arg, int status, int timeouts, r->error = grpc_error_add_child(error, r->error); } } - gpr_mu_unlock(&r->mu); - grpc_ares_request_unref(NULL, r); + grpc_ares_request_unref(&exec_ctx, r); + grpc_exec_ctx_finish(&exec_ctx); } -void grpc_resolve_address_ares_impl(grpc_exec_ctx *exec_ctx, const char *name, - const char *default_port, - grpc_pollset_set *interested_parties, - grpc_closure *on_done, - grpc_resolved_addresses **addrs) { +static void resolve_address_ares_impl(grpc_exec_ctx *exec_ctx, const char *name, + const char *default_port, + grpc_pollset_set *interested_parties, + grpc_closure *on_done, void **addrs, + bool is_lb_addrs_out) { /* TODO(zyc): Enable tracing after #9603 is checked in */ /* if (grpc_dns_trace) { gpr_log(GPR_DEBUG, "resolve_address (blocking): name=%s, default_port=%s", @@ -235,19 +375,33 @@ void grpc_resolve_address_ares_impl(grpc_exec_ctx *exec_ctx, const char *name, gpr_mu_init(&r->mu); r->ev_driver = ev_driver; r->on_done = on_done; - r->addrs_out = addrs; - r->default_port = gpr_strdup(default_port); - r->port = port; - r->host = host; + r->lb_addrs_out = is_lb_addrs_out; + if (is_lb_addrs_out) { + r->addrs_out.lb_addrs = (grpc_lb_addresses **)addrs; + } else { + r->addrs_out.addrs = (grpc_resolved_addresses **)addrs; + } r->success = false; r->error = GRPC_ERROR_NONE; ares_channel *channel = grpc_ares_ev_driver_get_channel(r->ev_driver); - gpr_ref_init(&r->pending_queries, 2); + gpr_ref_init(&r->pending_queries, 1); if (grpc_ipv6_loopback_available()) { - gpr_ref(&r->pending_queries); - ares_gethostbyname(*channel, r->host, AF_INET6, on_done_cb, r); + grpc_ares_hostbyname_request *hr = create_hostbyname_request( + r, host, strhtons(port), false /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET6, on_hostbyname_done_cb, hr); + } + grpc_ares_hostbyname_request *hr = create_hostbyname_request( + r, host, strhtons(port), false /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET, on_hostbyname_done_cb, hr); + if (is_lb_addrs_out) { + /* Query the SRV record */ + grpc_ares_request_ref(r); + char *service_name; + gpr_asprintf(&service_name, "_grpclb._tcp.%s", host); + ares_query(*channel, service_name, ns_c_in, ns_t_srv, on_srv_query_done_cb, + r); + gpr_free(service_name); } - ares_gethostbyname(*channel, r->host, AF_INET, on_done_cb, r); /* TODO(zyc): Handle CNAME records here. */ grpc_ares_ev_driver_start(exec_ctx, r->ev_driver); grpc_ares_request_unref(exec_ctx, r); @@ -258,6 +412,26 @@ error_cleanup: gpr_free(port); } +void grpc_resolve_address_ares_impl(grpc_exec_ctx *exec_ctx, const char *name, + const char *default_port, + grpc_pollset_set *interested_parties, + grpc_closure *on_done, + grpc_resolved_addresses **addrs) { + resolve_address_ares_impl(exec_ctx, name, default_port, interested_parties, + on_done, (void **)addrs, + false /* is_lb_addrs_out */); +} + +void grpc_resolve_grpclb_address_ares(grpc_exec_ctx *exec_ctx, const char *name, + const char *default_port, + grpc_pollset_set *interested_parties, + grpc_closure *on_done, + grpc_lb_addresses **addrs) { + resolve_address_ares_impl(exec_ctx, name, default_port, interested_parties, + on_done, (void **)addrs, + true /* is_lb_addrs_out */); +} + void (*grpc_resolve_address_ares)( grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, grpc_pollset_set *interested_parties, grpc_closure *on_done, diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h index 3dd40ea2687b7fec3a45d68f21a61206111aea96..1450ef373d0e86788fce6d5c6954854a10014836 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h @@ -34,6 +34,7 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H +#include "src/core/ext/filters/client_channel/lb_policy_factory.h" #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/iomgr.h" #include "src/core/lib/iomgr/polling_entity.h" @@ -51,6 +52,19 @@ extern void (*grpc_resolve_address_ares)(grpc_exec_ctx *exec_ctx, grpc_closure *on_done, grpc_resolved_addresses **addresses); +/* Asynchronously resolve addr. It will try to resolve grpclb SRV records in + addition to the normal address records. For normal address records, it uses + \a default_port if a port isn't designated in \a addr, otherwise it uses the + port in \a addr. grpc_ares_init() must be called at least once before this + function. \a on_done may be called directly in this function without being + scheduled with \a exec_ctx, it must not try to acquire locks that are being + held by the caller. */ +void grpc_resolve_grpclb_address_ares(grpc_exec_ctx *exec_ctx, const char *addr, + const char *default_port, + grpc_pollset_set *interested_parties, + grpc_closure *on_done, + grpc_lb_addresses **addresses); + /* Initialize gRPC ares wrapper. Must be called at least once before grpc_resolve_address_ares(). */ grpc_error *grpc_ares_init(void);