aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core
diff options
context:
space:
mode:
authorGravatar Mark D. Roth <roth@google.com>2017-08-03 14:47:23 -0700
committerGravatar Mark D. Roth <roth@google.com>2017-08-03 14:47:23 -0700
commitf9bf428489662be3d61391d44d7e13eb8f4b7018 (patch)
tree203692808f48ab94c45cb7b9eca2d78c17092498 /src/core
parente252e5fd9dcd8b4bf90c4c1efe18145509e150b9 (diff)
Add support for service configs to c-ares resolver.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c125
-rw-r--r--src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c86
-rw-r--r--src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h27
-rw-r--r--src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c7
-rw-r--r--src/core/lib/iomgr/gethostname.h26
-rw-r--r--src/core/lib/iomgr/gethostname_fallback.c27
-rw-r--r--src/core/lib/iomgr/gethostname_host_name_max.c37
-rw-r--r--src/core/lib/iomgr/gethostname_sysconf.c37
-rw-r--r--src/core/lib/iomgr/port.h9
9 files changed, 353 insertions, 28 deletions
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 04a7852323..f1480bb1ae 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
@@ -19,7 +19,10 @@
#include <grpc/support/port_platform.h>
#if GRPC_ARES == 1 && !defined(GRPC_UV)
+#include <limits.h>
+#include <stdio.h>
#include <string.h>
+#include <unistd.h>
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
@@ -31,11 +34,14 @@
#include "src/core/ext/filters/client_channel/resolver_registry.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/gethostname.h"
#include "src/core/lib/iomgr/resolve_address.h"
#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/json/json.h"
#include "src/core/lib/support/backoff.h"
#include "src/core/lib/support/env.h"
#include "src/core/lib/support/string.h"
+#include "src/core/lib/transport/service_config.h"
#define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1
#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
@@ -54,6 +60,8 @@ typedef struct {
char *default_port;
/** channel args. */
grpc_channel_args *channel_args;
+ /** whether to request the service config */
+ bool request_service_config;
/** pollset_set to drive the name resolution process */
grpc_pollset_set *interested_parties;
@@ -85,6 +93,8 @@ typedef struct {
/** currently resolving addresses */
grpc_lb_addresses *lb_addresses;
+ /** currently resolving service config */
+ char *service_config_json;
} ares_dns_resolver;
static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r);
@@ -144,6 +154,77 @@ static void dns_ares_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg,
GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "retry-timer");
}
+static bool value_in_json_array(grpc_json *array, const char *value) {
+ for (grpc_json *entry = array->child; entry != NULL; entry = entry->next) {
+ if (entry->type == GRPC_JSON_STRING && strcmp(entry->value, value) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static char *choose_service_config(char *service_config_choice_json) {
+ grpc_json *choices_json = grpc_json_parse_string(service_config_choice_json);
+ if (choices_json == NULL || choices_json->type != GRPC_JSON_ARRAY) {
+ gpr_log(GPR_ERROR, "cannot parse service config JSON string");
+ return NULL;
+ }
+ char *service_config = NULL;
+ for (grpc_json *choice = choices_json->child; choice != NULL;
+ choice = choice->next) {
+ if (choice->type != GRPC_JSON_OBJECT) {
+ gpr_log(GPR_ERROR, "cannot parse service config JSON string");
+ break;
+ }
+ grpc_json *service_config_json = NULL;
+ for (grpc_json *field = choice->child; field != NULL; field = field->next) {
+ // Check client language, if specified.
+ if (strcmp(field->key, "clientLanguage") == 0) {
+ if (field->type != GRPC_JSON_ARRAY ||
+ !value_in_json_array(field, "c++")) {
+ service_config_json = NULL;
+ break;
+ }
+ }
+ // Check client hostname, if specified.
+ if (strcmp(field->key, "clientHostname") == 0) {
+ char *hostname = grpc_gethostname();
+ if (hostname == NULL || field->type != GRPC_JSON_ARRAY ||
+ !value_in_json_array(field, hostname)) {
+ service_config_json = NULL;
+ break;
+ }
+ }
+ // Check percentage, if specified.
+ if (strcmp(field->key, "percentage") == 0) {
+ if (field->type != GRPC_JSON_NUMBER) {
+ service_config_json = NULL;
+ break;
+ }
+ int random_pct = rand() % 100;
+ int percentage;
+ if (sscanf(field->value, "%d", &percentage) != 1 ||
+ random_pct > percentage) {
+ service_config_json = NULL;
+ break;
+ }
+ }
+ // Save service config.
+ if (strcmp(field->key, "serviceConfig") == 0) {
+ if (field->type == GRPC_JSON_OBJECT) {
+ service_config_json = field;
+ }
+ }
+ }
+ if (service_config_json != NULL) {
+ service_config = grpc_json_dump_to_string(service_config_json, 0);
+ break;
+ }
+ }
+ grpc_json_destroy(choices_json);
+ return service_config;
+}
+
static void dns_ares_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) {
ares_dns_resolver *r = arg;
@@ -152,8 +233,40 @@ static void dns_ares_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg,
r->resolving = false;
r->pending_request = NULL;
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);
+ static const char *args_to_remove[2];
+ size_t num_args_to_remove = 0;
+ grpc_arg new_args[3];
+ size_t num_args_to_add = 0;
+ new_args[num_args_to_add++] =
+ grpc_lb_addresses_create_channel_arg(r->lb_addresses);
+ grpc_service_config *service_config = NULL;
+ char *service_config_string = NULL;
+ if (r->service_config_json != NULL) {
+ service_config_string = choose_service_config(r->service_config_json);
+ gpr_free(r->service_config_json);
+ if (service_config_string != NULL) {
+ gpr_log(GPR_INFO, "selected service config choice: %s",
+ service_config_string);
+ args_to_remove[num_args_to_remove++] = GRPC_ARG_SERVICE_CONFIG;
+ new_args[num_args_to_add++] = grpc_channel_arg_string_create(
+ GRPC_ARG_SERVICE_CONFIG, service_config_string);
+ service_config = grpc_service_config_create(service_config_string);
+ if (service_config != NULL) {
+ const char *lb_policy_name =
+ grpc_service_config_get_lb_policy_name(service_config);
+ if (lb_policy_name != NULL) {
+ args_to_remove[num_args_to_remove++] = GRPC_ARG_LB_POLICY_NAME;
+ new_args[num_args_to_add++] = grpc_channel_arg_string_create(
+ GRPC_ARG_LB_POLICY_NAME, (char *)lb_policy_name);
+ }
+ }
+ }
+ }
+ result = grpc_channel_args_copy_and_add_and_remove(
+ r->channel_args, args_to_remove, num_args_to_remove, new_args,
+ num_args_to_add);
+ if (service_config != NULL) grpc_service_config_destroy(service_config);
+ gpr_free(service_config_string);
grpc_lb_addresses_destroy(exec_ctx, r->lb_addresses);
} else {
const char *msg = grpc_error_string(error);
@@ -207,10 +320,12 @@ static void dns_ares_start_resolving_locked(grpc_exec_ctx *exec_ctx,
GPR_ASSERT(!r->resolving);
r->resolving = true;
r->lb_addresses = NULL;
+ r->service_config_json = NULL;
r->pending_request = grpc_dns_lookup_ares(
exec_ctx, r->dns_server, r->name_to_resolve, r->default_port,
r->interested_parties, &r->dns_ares_on_resolved_locked, &r->lb_addresses,
- true /* check_grpclb */);
+ true /* check_grpclb */,
+ r->request_service_config ? &r->service_config_json : NULL);
}
static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx,
@@ -256,6 +371,10 @@ static grpc_resolver *dns_ares_create(grpc_exec_ctx *exec_ctx,
r->name_to_resolve = gpr_strdup(path);
r->default_port = gpr_strdup(default_port);
r->channel_args = grpc_channel_args_copy(args->args);
+ const grpc_arg *arg = grpc_channel_args_find(
+ r->channel_args, GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION);
+ r->request_service_config = !grpc_channel_arg_get_integer(
+ arg, (grpc_integer_options){false, false, true});
r->interested_parties = grpc_pollset_set_create();
if (args->pollset_set != NULL) {
grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties,
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 6ec3790a5f..e65723a63b 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
@@ -54,6 +54,8 @@ struct grpc_ares_request {
grpc_closure *on_done;
/** the pointer to receive the resolved addresses */
grpc_lb_addresses **lb_addrs_out;
+ /** the pointer to receive the service config in JSON */
+ char **service_config_json_out;
/** the evernt driver used by this request */
grpc_ares_ev_driver *ev_driver;
/** number of ongoing queries */
@@ -266,10 +268,68 @@ static void on_srv_query_done_cb(void *arg, int status, int timeouts,
grpc_exec_ctx_finish(&exec_ctx);
}
+static const char g_service_config_attribute_prefix[] = "grpc_config=";
+
+static void on_txt_done_cb(void *arg, int status, int timeouts,
+ unsigned char *buf, int len) {
+ gpr_log(GPR_DEBUG, "on_txt_done_cb");
+ char *error_msg;
+ grpc_ares_request *r = (grpc_ares_request *)arg;
+ gpr_mu_lock(&r->mu);
+ if (status != ARES_SUCCESS) goto fail;
+ struct ares_txt_ext *reply = NULL;
+ status = ares_parse_txt_reply_ext(buf, len, &reply);
+ if (status != ARES_SUCCESS) goto fail;
+ // Find service config in TXT record.
+ const size_t prefix_len = sizeof(g_service_config_attribute_prefix) - 1;
+ struct ares_txt_ext *result;
+ for (result = reply; result != NULL; result = result->next) {
+ if (result->record_start &&
+ memcmp(result->txt, g_service_config_attribute_prefix, prefix_len) ==
+ 0) {
+ break;
+ }
+ }
+ // Found a service config record.
+ if (result != NULL) {
+ size_t service_config_len = result->length - prefix_len;
+ *r->service_config_json_out = gpr_malloc(service_config_len + 1);
+ memcpy(*r->service_config_json_out, result->txt + prefix_len,
+ service_config_len);
+ for (result = result->next; result != NULL && !result->record_start;
+ result = result->next) {
+ *r->service_config_json_out = gpr_realloc(
+ *r->service_config_json_out, service_config_len + result->length + 1);
+ memcpy(*r->service_config_json_out + service_config_len, result->txt,
+ result->length);
+ service_config_len += result->length;
+ }
+ (*r->service_config_json_out)[service_config_len] = '\0';
+ gpr_log(GPR_INFO, "found service config: %s", *r->service_config_json_out);
+ }
+ // Clean up.
+ ares_free_data(reply);
+ goto done;
+fail:
+ gpr_asprintf(&error_msg, "C-ares TXT lookup 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);
+ }
+done:
+ gpr_mu_unlock(&r->mu);
+ grpc_ares_request_unref(NULL, r);
+}
+
static grpc_ares_request *grpc_dns_lookup_ares_impl(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties,
- grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb) {
+ grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb,
+ char **service_config_json) {
grpc_error *error = GRPC_ERROR_NONE;
/* TODO(zyc): Enable tracing after #9603 is checked in */
/* if (grpc_dns_trace) {
@@ -300,11 +360,12 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl(
error = grpc_ares_ev_driver_create(&ev_driver, interested_parties);
if (error != GRPC_ERROR_NONE) goto error_cleanup;
- grpc_ares_request *r = gpr_malloc(sizeof(grpc_ares_request));
+ grpc_ares_request *r = gpr_zalloc(sizeof(grpc_ares_request));
gpr_mu_init(&r->mu);
r->ev_driver = ev_driver;
r->on_done = on_done;
r->lb_addrs_out = addrs;
+ r->service_config_json_out = service_config_json;
r->success = false;
r->error = GRPC_ERROR_NONE;
ares_channel *channel = grpc_ares_ev_driver_get_channel(r->ev_driver);
@@ -315,13 +376,17 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl(
grpc_resolved_address addr;
if (grpc_parse_ipv4_hostport(dns_server, &addr, false /* log_errors */)) {
r->dns_server_addr.family = AF_INET;
- memcpy(&r->dns_server_addr.addr.addr4, addr.addr, addr.len);
+ struct sockaddr_in *in = (struct sockaddr_in *)addr.addr;
+ memcpy(&r->dns_server_addr.addr.addr4, &in->sin_addr,
+ sizeof(struct in_addr));
r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr);
r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr);
} else if (grpc_parse_ipv6_hostport(dns_server, &addr,
false /* log_errors */)) {
r->dns_server_addr.family = AF_INET6;
- memcpy(&r->dns_server_addr.addr.addr6, addr.addr, addr.len);
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr.addr;
+ memcpy(&r->dns_server_addr.addr.addr6, &in6->sin6_addr,
+ sizeof(struct in6_addr));
r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr);
r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr);
} else {
@@ -342,8 +407,6 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl(
goto error_cleanup;
}
}
- // An extra reference is put here to avoid destroying the request in
- // on_done_cb before calling grpc_ares_ev_driver_start.
gpr_ref_init(&r->pending_queries, 1);
if (grpc_ipv6_loopback_available()) {
grpc_ares_hostbyname_request *hr = create_hostbyname_request(
@@ -362,6 +425,10 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl(
r);
gpr_free(service_name);
}
+ if (service_config_json != NULL) {
+ grpc_ares_request_ref(r);
+ ares_search(*channel, hr->host, ns_c_in, ns_t_txt, on_txt_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);
@@ -379,8 +446,8 @@ error_cleanup:
grpc_ares_request *(*grpc_dns_lookup_ares)(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties,
- grpc_closure *on_done, grpc_lb_addresses **addrs,
- bool check_grpclb) = grpc_dns_lookup_ares_impl;
+ grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb,
+ char **service_config_json) = grpc_dns_lookup_ares_impl;
void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) {
if (grpc_dns_lookup_ares == grpc_dns_lookup_ares_impl) {
@@ -465,7 +532,8 @@ static void grpc_resolve_address_ares_impl(grpc_exec_ctx *exec_ctx,
grpc_schedule_on_exec_ctx);
grpc_dns_lookup_ares(exec_ctx, NULL /* dns_server */, name, default_port,
interested_parties, &r->on_dns_lookup_done, &r->lb_addrs,
- false /* check_grpclb */);
+ false /* check_grpclb */,
+ NULL /* service_config_json */);
}
void (*grpc_resolve_address_ares)(
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 5d2d6c993b..108333047d 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
@@ -27,29 +27,30 @@
typedef struct grpc_ares_request grpc_ares_request;
-/* Asynchronously resolve addr. Use \a default_port if a port isn't designated
- in addr, otherwise use the port in 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. */
+/* Asynchronously resolve \a name. Use \a default_port if a port isn't
+ designated in \a name, otherwise use the port in \a name. 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,
+ so it must not try to acquire locks that are being held by the caller. */
extern void (*grpc_resolve_address_ares)(grpc_exec_ctx *exec_ctx,
- const char *addr,
+ const char *name,
const char *default_port,
grpc_pollset_set *interested_parties,
grpc_closure *on_done,
grpc_resolved_addresses **addresses);
-/* Asynchronously resolve addr. It will try to resolve grpclb SRV records in
+/* Asynchronously resolve \a name. 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
+ \a default_port if a port isn't designated in \a name, otherwise it uses the
+ port in \a name. 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. */
+ scheduled with \a exec_ctx, so it must not try to acquire locks that are
+ being held by the caller. */
extern grpc_ares_request *(*grpc_dns_lookup_ares)(
- grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr,
+ grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties,
- grpc_closure *on_done, grpc_lb_addresses **addresses, bool check_grpclb);
+ grpc_closure *on_done, grpc_lb_addresses **addresses, bool check_grpclb,
+ char **service_config_json);
/* Cancel the pending grpc_ares_request \a request */
void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c
index b67636a3e4..f2587c4520 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c
@@ -28,15 +28,16 @@ struct grpc_ares_request {
static grpc_ares_request *grpc_dns_lookup_ares_impl(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties,
- grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb) {
+ grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb,
+ char **service_config_json) {
return NULL;
}
grpc_ares_request *(*grpc_dns_lookup_ares)(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties,
- grpc_closure *on_done, grpc_lb_addresses **addrs,
- bool check_grpclb) = grpc_dns_lookup_ares_impl;
+ grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb,
+ char **service_config_json) = grpc_dns_lookup_ares_impl;
void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) {}
diff --git a/src/core/lib/iomgr/gethostname.h b/src/core/lib/iomgr/gethostname.h
new file mode 100644
index 0000000000..9c6b9d8d42
--- /dev/null
+++ b/src/core/lib/iomgr/gethostname.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2017 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H
+#define GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H
+
+// Returns the hostname of the local machine.
+// Caller takes ownership of result.
+char *grpc_gethostname();
+
+#endif /* GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H */
diff --git a/src/core/lib/iomgr/gethostname_fallback.c b/src/core/lib/iomgr/gethostname_fallback.c
new file mode 100644
index 0000000000..6229461568
--- /dev/null
+++ b/src/core/lib/iomgr/gethostname_fallback.c
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright 2017 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 "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_GETHOSTNAME_FALLBACK
+
+#include <stddef.h>
+
+char *grpc_gethostname() { return NULL; }
+
+#endif // GRPC_GETHOSTNAME_FALLBACK
diff --git a/src/core/lib/iomgr/gethostname_host_name_max.c b/src/core/lib/iomgr/gethostname_host_name_max.c
new file mode 100644
index 0000000000..4d0511412e
--- /dev/null
+++ b/src/core/lib/iomgr/gethostname_host_name_max.c
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright 2017 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 "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_HOST_NAME_MAX
+
+#include <limits.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+
+char *grpc_gethostname() {
+ char *hostname = (char *)gpr_malloc(HOST_NAME_MAX);
+ if (gethostname(hostname, HOST_NAME_MAX) != 0) {
+ gpr_free(hostname);
+ return NULL;
+ }
+ return hostname;
+}
+
+#endif // GRPC_POSIX_HOST_NAME_MAX
diff --git a/src/core/lib/iomgr/gethostname_sysconf.c b/src/core/lib/iomgr/gethostname_sysconf.c
new file mode 100644
index 0000000000..51bac5d69d
--- /dev/null
+++ b/src/core/lib/iomgr/gethostname_sysconf.c
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright 2017 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 "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SYSCONF
+
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+
+char *grpc_gethostname() {
+ size_t host_name_max = (size_t)sysconf(_SC_HOST_NAME_MAX);
+ char *hostname = (char *)gpr_malloc(host_name_max);
+ if (gethostname(hostname, host_name_max) != 0) {
+ gpr_free(hostname);
+ return NULL;
+ }
+ return hostname;
+}
+
+#endif // GRPC_POSIX_SYSCONF
diff --git a/src/core/lib/iomgr/port.h b/src/core/lib/iomgr/port.h
index c12058f890..42033d0ba4 100644
--- a/src/core/lib/iomgr/port.h
+++ b/src/core/lib/iomgr/port.h
@@ -59,6 +59,7 @@
#define GRPC_HAVE_MSG_NOSIGNAL 1
#define GRPC_HAVE_UNIX_SOCKET 1
#define GRPC_LINUX_MULTIPOLL_WITH_EPOLL 1
+#define GRPC_POSIX_HOST_NAME_MAX 1
#define GRPC_POSIX_SOCKET 1
#define GRPC_POSIX_SOCKETADDR 1
#define GRPC_POSIX_WAKEUP_FD 1
@@ -93,6 +94,7 @@
#define GRPC_POSIX_SOCKET 1
#define GRPC_POSIX_SOCKETADDR 1
#define GRPC_POSIX_SOCKETUTILS 1
+#define GRPC_POSIX_SYSCONF 1
#define GRPC_POSIX_WAKEUP_FD 1
#define GRPC_TIMER_USE_GENERIC 1
#elif defined(GPR_FREEBSD)
@@ -125,4 +127,11 @@
#error Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GPR_CUSTOM_SOCKET
#endif
+#if defined(GRPC_POSIX_HOST_NAME_MAX) && defined(GRPC_POSIX_SYSCONF)
+#error "Cannot define both GRPC_POSIX_HOST_NAME_MAX and GRPC_POSIX_SYSCONF"
+#endif
+#if !defined(GRPC_POSIX_HOST_NAME_MAX) && !defined(GRPC_POSIX_SYSCONF)
+#define GRPC_GETHOSTNAME_FALLBACK 1
+#endif
+
#endif /* GRPC_CORE_LIB_IOMGR_PORT_H */