Merge branch 'flow_control_buf_fix' of github.com:yang-g/grpc into virtuous-velvit-velociraptor
diff --git a/BUILD b/BUILD
index e116d45..dccb0e4 100644
--- a/BUILD
+++ b/BUILD
@@ -168,7 +168,7 @@
     "src/core/client_config/resolver_factory.h",
     "src/core/client_config/resolver_registry.h",
     "src/core/client_config/resolvers/dns_resolver.h",
-    "src/core/client_config/resolvers/unix_resolver_posix.h",
+    "src/core/client_config/resolvers/sockaddr_resolver.h",
     "src/core/client_config/subchannel.h",
     "src/core/client_config/subchannel_factory.h",
     "src/core/client_config/uri_parser.h",
@@ -246,6 +246,7 @@
     "src/core/transport/transport.h",
     "src/core/transport/transport_impl.h",
     "src/core/census/context.h",
+    "src/core/census/rpc_stat_id.h",
     "src/core/httpcli/format_request.c",
     "src/core/httpcli/httpcli.c",
     "src/core/httpcli/httpcli_security_connector.c",
@@ -287,7 +288,7 @@
     "src/core/client_config/resolver_factory.c",
     "src/core/client_config/resolver_registry.c",
     "src/core/client_config/resolvers/dns_resolver.c",
-    "src/core/client_config/resolvers/unix_resolver_posix.c",
+    "src/core/client_config/resolvers/sockaddr_resolver.c",
     "src/core/client_config/subchannel.c",
     "src/core/client_config/subchannel_factory.c",
     "src/core/client_config/uri_parser.c",
@@ -381,6 +382,7 @@
     "src/core/transport/transport_op_string.c",
     "src/core/census/context.c",
     "src/core/census/initialize.c",
+    "src/core/census/record_stat.c",
   ],
   hdrs = [
     "include/grpc/grpc_security.h",
@@ -424,7 +426,7 @@
     "src/core/client_config/resolver_factory.h",
     "src/core/client_config/resolver_registry.h",
     "src/core/client_config/resolvers/dns_resolver.h",
-    "src/core/client_config/resolvers/unix_resolver_posix.h",
+    "src/core/client_config/resolvers/sockaddr_resolver.h",
     "src/core/client_config/subchannel.h",
     "src/core/client_config/subchannel_factory.h",
     "src/core/client_config/uri_parser.h",
@@ -502,6 +504,7 @@
     "src/core/transport/transport.h",
     "src/core/transport/transport_impl.h",
     "src/core/census/context.h",
+    "src/core/census/rpc_stat_id.h",
     "src/core/surface/init_unsecure.c",
     "src/core/census/grpc_context.c",
     "src/core/channel/channel_args.c",
@@ -520,7 +523,7 @@
     "src/core/client_config/resolver_factory.c",
     "src/core/client_config/resolver_registry.c",
     "src/core/client_config/resolvers/dns_resolver.c",
-    "src/core/client_config/resolvers/unix_resolver_posix.c",
+    "src/core/client_config/resolvers/sockaddr_resolver.c",
     "src/core/client_config/subchannel.c",
     "src/core/client_config/subchannel_factory.c",
     "src/core/client_config/uri_parser.c",
@@ -614,6 +617,7 @@
     "src/core/transport/transport_op_string.c",
     "src/core/census/context.c",
     "src/core/census/initialize.c",
+    "src/core/census/record_stat.c",
   ],
   hdrs = [
     "include/grpc/byte_buffer.h",
@@ -998,7 +1002,7 @@
     "src/core/client_config/resolver_factory.c",
     "src/core/client_config/resolver_registry.c",
     "src/core/client_config/resolvers/dns_resolver.c",
-    "src/core/client_config/resolvers/unix_resolver_posix.c",
+    "src/core/client_config/resolvers/sockaddr_resolver.c",
     "src/core/client_config/subchannel.c",
     "src/core/client_config/subchannel_factory.c",
     "src/core/client_config/uri_parser.c",
@@ -1092,6 +1096,7 @@
     "src/core/transport/transport_op_string.c",
     "src/core/census/context.c",
     "src/core/census/initialize.c",
+    "src/core/census/record_stat.c",
   ],
   hdrs = [
     "include/grpc/grpc_security.h",
@@ -1137,7 +1142,7 @@
     "src/core/client_config/resolver_factory.h",
     "src/core/client_config/resolver_registry.h",
     "src/core/client_config/resolvers/dns_resolver.h",
-    "src/core/client_config/resolvers/unix_resolver_posix.h",
+    "src/core/client_config/resolvers/sockaddr_resolver.h",
     "src/core/client_config/subchannel.h",
     "src/core/client_config/subchannel_factory.h",
     "src/core/client_config/uri_parser.h",
@@ -1215,6 +1220,7 @@
     "src/core/transport/transport.h",
     "src/core/transport/transport_impl.h",
     "src/core/census/context.h",
+    "src/core/census/rpc_stat_id.h",
   ],
   includes = [
     "include",
diff --git a/INSTALL b/INSTALL
index 5edb5e6..8a0a98a 100644
--- a/INSTALL
+++ b/INSTALL
@@ -117,7 +117,7 @@
 Then execute the following for all the needed build dependencies
 
   $ sudo /opt/local/bin/port install autoconf automake libtool gflags cmake
-  $ mkdir ~/gtest
+  $ mkdir ~/gtest-svn
   $ svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn
   $ mkdir mybuild
   $ cd mybuild
diff --git a/Makefile b/Makefile
index 2ae0cd0..083c01a 100644
--- a/Makefile
+++ b/Makefile
@@ -3517,7 +3517,7 @@
     src/core/client_config/resolver_factory.c \
     src/core/client_config/resolver_registry.c \
     src/core/client_config/resolvers/dns_resolver.c \
-    src/core/client_config/resolvers/unix_resolver_posix.c \
+    src/core/client_config/resolvers/sockaddr_resolver.c \
     src/core/client_config/subchannel.c \
     src/core/client_config/subchannel_factory.c \
     src/core/client_config/uri_parser.c \
@@ -3611,6 +3611,7 @@
     src/core/transport/transport_op_string.c \
     src/core/census/context.c \
     src/core/census/initialize.c \
+    src/core/census/record_stat.c \
 
 PUBLIC_HEADERS_C += \
     include/grpc/grpc_security.h \
@@ -3781,7 +3782,7 @@
     src/core/client_config/resolver_factory.c \
     src/core/client_config/resolver_registry.c \
     src/core/client_config/resolvers/dns_resolver.c \
-    src/core/client_config/resolvers/unix_resolver_posix.c \
+    src/core/client_config/resolvers/sockaddr_resolver.c \
     src/core/client_config/subchannel.c \
     src/core/client_config/subchannel_factory.c \
     src/core/client_config/uri_parser.c \
@@ -3875,6 +3876,7 @@
     src/core/transport/transport_op_string.c \
     src/core/census/context.c \
     src/core/census/initialize.c \
+    src/core/census/record_stat.c \
 
 PUBLIC_HEADERS_C += \
     include/grpc/byte_buffer.h \
diff --git a/build.json b/build.json
index 2755703..f06c6cb 100644
--- a/build.json
+++ b/build.json
@@ -18,11 +18,13 @@
         "include/grpc/census.h"
       ],
       "headers": [
-        "src/core/census/context.h"
+        "src/core/census/context.h",
+        "src/core/census/rpc_stat_id.h"
       ],
       "src": [
         "src/core/census/context.c",
-        "src/core/census/initialize.c"
+        "src/core/census/initialize.c",
+        "src/core/census/record_stat.c"
       ]
     },
     {
@@ -129,7 +131,7 @@
         "src/core/client_config/resolver_factory.h",
         "src/core/client_config/resolver_registry.h",
         "src/core/client_config/resolvers/dns_resolver.h",
-        "src/core/client_config/resolvers/unix_resolver_posix.h",
+        "src/core/client_config/resolvers/sockaddr_resolver.h",
         "src/core/client_config/subchannel.h",
         "src/core/client_config/subchannel_factory.h",
         "src/core/client_config/uri_parser.h",
@@ -225,7 +227,7 @@
         "src/core/client_config/resolver_factory.c",
         "src/core/client_config/resolver_registry.c",
         "src/core/client_config/resolvers/dns_resolver.c",
-        "src/core/client_config/resolvers/unix_resolver_posix.c",
+        "src/core/client_config/resolvers/sockaddr_resolver.c",
         "src/core/client_config/subchannel.c",
         "src/core/client_config/subchannel_factory.c",
         "src/core/client_config/uri_parser.c",
diff --git a/gRPC.podspec b/gRPC.podspec
index 28d9ac4..f47b44f 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -170,7 +170,7 @@
                       'src/core/client_config/resolver_factory.h',
                       'src/core/client_config/resolver_registry.h',
                       'src/core/client_config/resolvers/dns_resolver.h',
-                      'src/core/client_config/resolvers/unix_resolver_posix.h',
+                      'src/core/client_config/resolvers/sockaddr_resolver.h',
                       'src/core/client_config/subchannel.h',
                       'src/core/client_config/subchannel_factory.h',
                       'src/core/client_config/uri_parser.h',
@@ -248,6 +248,7 @@
                       'src/core/transport/transport.h',
                       'src/core/transport/transport_impl.h',
                       'src/core/census/context.h',
+                      'src/core/census/rpc_stat_id.h',
                       'grpc/grpc_security.h',
                       'grpc/byte_buffer.h',
                       'grpc/byte_buffer_reader.h',
@@ -296,7 +297,7 @@
                       'src/core/client_config/resolver_factory.c',
                       'src/core/client_config/resolver_registry.c',
                       'src/core/client_config/resolvers/dns_resolver.c',
-                      'src/core/client_config/resolvers/unix_resolver_posix.c',
+                      'src/core/client_config/resolvers/sockaddr_resolver.c',
                       'src/core/client_config/subchannel.c',
                       'src/core/client_config/subchannel_factory.c',
                       'src/core/client_config/uri_parser.c',
@@ -389,7 +390,8 @@
                       'src/core/transport/transport.c',
                       'src/core/transport/transport_op_string.c',
                       'src/core/census/context.c',
-                      'src/core/census/initialize.c'
+                      'src/core/census/initialize.c',
+                      'src/core/census/record_stat.c'
 
     ss.private_header_files = 'src/core/support/env.h',
                               'src/core/support/file.h',
@@ -434,7 +436,7 @@
                               'src/core/client_config/resolver_factory.h',
                               'src/core/client_config/resolver_registry.h',
                               'src/core/client_config/resolvers/dns_resolver.h',
-                              'src/core/client_config/resolvers/unix_resolver_posix.h',
+                              'src/core/client_config/resolvers/sockaddr_resolver.h',
                               'src/core/client_config/subchannel.h',
                               'src/core/client_config/subchannel_factory.h',
                               'src/core/client_config/uri_parser.h',
@@ -511,7 +513,8 @@
                               'src/core/transport/stream_op.h',
                               'src/core/transport/transport.h',
                               'src/core/transport/transport_impl.h',
-                              'src/core/census/context.h'
+                              'src/core/census/context.h',
+                              'src/core/census/rpc_stat_id.h'
 
     ss.header_mappings_dir = '.'
 
diff --git a/include/grpc/census.h b/include/grpc/census.h
index 3fc07af..3797839 100644
--- a/include/grpc/census.h
+++ b/include/grpc/census.h
@@ -100,6 +100,17 @@
  * future census calls will result in undefined behavior. */
 void census_context_destroy(census_context *context);
 
+/* A census statistic to be recorded comprises two parts: an ID for the
+ * particular statistic and the value to be recorded against it. */
+typedef struct {
+  int id;
+  double value;
+} census_stat;
+
+/* Record new stats against the given context. */
+void census_record_stat(census_context *context, census_stat *stats,
+                        size_t nstats);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index 504f0cc..b05e4d6 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -433,6 +433,20 @@
 grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
                                       size_t nops, void *tag);
 
+/** Returns a newly allocated string representing the endpoint to which this
+    call is communicating with. The string is in the uri format accepted by
+    grpc_channel_create.
+    The returned string should be disposed of with gpr_free(). 
+
+    WARNING: this value is never authenticated or subject to any security
+    related code. It must not be used for any authentication related
+    functionality. Instead, use grpc_auth_context. */
+char *grpc_call_get_peer(grpc_call *call);
+
+/** Return a newly allocated string representing the target a channel was
+    created for. */
+char *grpc_channel_get_target(grpc_channel *channel);
+
 /** Create a client channel to 'target'. Additional channel level configuration
     MAY be provided by grpc_channel_args, though the expectation is that most
     clients will want to simply pass NULL. See grpc_channel_args definition for
@@ -442,7 +456,7 @@
                                   const grpc_channel_args *args);
 
 /** Create a lame client: this client fails every operation attempted on it. */
-grpc_channel *grpc_lame_client_channel_create(void);
+grpc_channel *grpc_lame_client_channel_create(const char *target);
 
 /** Close and destroy a grpc channel */
 void grpc_channel_destroy(grpc_channel *channel);
diff --git a/include/grpc/support/host_port.h b/include/grpc/support/host_port.h
index 3cc2f49..30267ab 100644
--- a/include/grpc/support/host_port.h
+++ b/include/grpc/support/host_port.h
@@ -52,8 +52,10 @@
 
 /* Given a name in the form "host:port" or "[ho:st]:port", split into hostname
    and port number, into newly allocated strings, which must later be
-   destroyed using gpr_free(). */
-void gpr_split_host_port(const char *name, char **host, char **port);
+   destroyed using gpr_free().
+   Return 1 on success, 0 on failure. Guarantees *host and *port == NULL on
+   failure. */
+int gpr_split_host_port(const char *name, char **host, char **port);
 
 #ifdef __cplusplus
 }
diff --git a/src/core/client_config/resolvers/unix_resolver_posix.h b/src/core/census/record_stat.c
similarity index 78%
copy from src/core/client_config/resolvers/unix_resolver_posix.h
copy to src/core/census/record_stat.c
index 57ace59..3dd9186 100644
--- a/src/core/client_config/resolvers/unix_resolver_posix.h
+++ b/src/core/census/record_stat.c
@@ -31,14 +31,8 @@
  *
  */
 
-#ifndef GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H
-#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H
+#include <grpc/census.h>
+#include "src/core/census/rpc_stat_id.h"
 
-#include <grpc/support/port_platform.h>
-
-#include "src/core/client_config/resolver_factory.h"
-
-/** Create a unix resolver factory */
-grpc_resolver_factory *grpc_unix_resolver_factory_create(void);
-
-#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H */
+void census_record_stat(census_context *context, census_stat *stats,
+                        size_t nstats) {}
diff --git a/src/core/client_config/resolvers/unix_resolver_posix.h b/src/core/census/rpc_stat_id.h
similarity index 71%
copy from src/core/client_config/resolvers/unix_resolver_posix.h
copy to src/core/census/rpc_stat_id.h
index 57ace59..fc0aa6f 100644
--- a/src/core/client_config/resolvers/unix_resolver_posix.h
+++ b/src/core/census/rpc_stat_id.h
@@ -31,14 +31,16 @@
  *
  */
 
-#ifndef GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H
-#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H
+#ifndef CENSUS_RPC_STAT_ID_H
+#define CENSUS_RPC_STAT_ID_H
 
-#include <grpc/support/port_platform.h>
+/* Stats ID's used for RPC measurements. */
+#define CENSUS_INVALID_STAT_ID 0     /* ID 0 is always invalid */
+#define CENSUS_RPC_CLIENT_REQUESTS 1 /* Count of client requests sent. */
+#define CENSUS_RPC_SERVER_REQUESTS 2 /* Count of server requests sent. */
+#define CENSUS_RPC_CLIENT_ERRORS 3   /* Client error counts. */
+#define CENSUS_RPC_SERVER_ERRORS 4   /* Server error counts. */
+#define CENSUS_RPC_CLIENT_LATENCY 5  /* Client side request latency. */
+#define CENSUS_RPC_SERVER_LATENCY 6  /* Server side request latency. */
 
-#include "src/core/client_config/resolver_factory.h"
-
-/** Create a unix resolver factory */
-grpc_resolver_factory *grpc_unix_resolver_factory_create(void);
-
-#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H */
+#endif /* CENSUS_RPC_STAT_ID_H */
diff --git a/src/core/channel/channel_stack.c b/src/core/channel/channel_stack.c
index e38dcb5..cd7c182 100644
--- a/src/core/channel/channel_stack.c
+++ b/src/core/channel/channel_stack.c
@@ -191,6 +191,11 @@
   next_elem->filter->start_transport_stream_op(next_elem, op);
 }
 
+char *grpc_call_next_get_peer(grpc_call_element *elem) {
+  grpc_call_element *next_elem = elem + 1;
+  return next_elem->filter->get_peer(next_elem);
+}
+
 void grpc_channel_next_op(grpc_channel_element *elem, grpc_transport_op *op) {
   grpc_channel_element *next_elem = elem + 1;
   next_elem->filter->start_transport_op(next_elem, op);
diff --git a/src/core/channel/channel_stack.h b/src/core/channel/channel_stack.h
index 785be89..4a608b9 100644
--- a/src/core/channel/channel_stack.h
+++ b/src/core/channel/channel_stack.h
@@ -104,6 +104,9 @@
      The filter does not need to do any chaining */
   void (*destroy_channel_elem)(grpc_channel_element *elem);
 
+  /* Implement grpc_call_get_peer() */
+  char *(*get_peer)(grpc_call_element *elem);
+
   /* The name of this filter */
   const char *name;
 } grpc_channel_filter;
@@ -173,6 +176,8 @@
 /* Call the next operation (depending on call directionality) in a channel
    stack */
 void grpc_channel_next_op(grpc_channel_element *elem, grpc_transport_op *op);
+/* Pass through a request to get_peer to the next child element */
+char *grpc_call_next_get_peer(grpc_call_element *elem);
 
 /* Given the top element of a channel stack, get the channel stack itself */
 grpc_channel_stack *grpc_channel_stack_from_top_element(
diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c
index c1aa580..108a6df 100644
--- a/src/core/channel/client_channel.c
+++ b/src/core/channel/client_channel.c
@@ -265,6 +265,26 @@
   return consumed_op;
 }
 
+static char *cc_get_peer(grpc_call_element *elem) {
+  call_data *calld = elem->call_data;
+  channel_data *chand = elem->channel_data;
+  grpc_subchannel_call *subchannel_call;
+  char *result;
+
+  gpr_mu_lock(&calld->mu_state);
+  if (calld->state == CALL_ACTIVE) {
+    subchannel_call = calld->subchannel_call;
+    GRPC_SUBCHANNEL_CALL_REF(subchannel_call, "get_peer");
+    gpr_mu_unlock(&calld->mu_state);
+    result = grpc_subchannel_call_get_peer(subchannel_call);
+    GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call, "get_peer");
+    return result;
+  } else {
+    gpr_mu_unlock(&calld->mu_state);
+    return grpc_channel_get_target(chand->master);
+  }
+}
+
 static void perform_transport_stream_op(grpc_call_element *elem,
                                         grpc_transport_stream_op *op,
                                         int continuation) {
@@ -590,6 +610,7 @@
     sizeof(channel_data),
     init_channel_elem,
     destroy_channel_elem,
+    cc_get_peer,
     "client-channel",
 };
 
diff --git a/src/core/channel/compress_filter.c b/src/core/channel/compress_filter.c
index bf02f92..14e8ca7 100644
--- a/src/core/channel/compress_filter.c
+++ b/src/core/channel/compress_filter.c
@@ -322,4 +322,5 @@
     sizeof(channel_data),
     init_channel_elem,
     destroy_channel_elem,
+    grpc_call_next_get_peer,
     "compress"};
diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c
index 34d07de..b95ed06 100644
--- a/src/core/channel/connected_channel.c
+++ b/src/core/channel/connected_channel.c
@@ -119,6 +119,11 @@
   grpc_transport_destroy(cd->transport);
 }
 
+static char *con_get_peer(grpc_call_element *elem) {
+  channel_data *chand = elem->channel_data;
+  return grpc_transport_get_peer(chand->transport);
+}
+
 const grpc_channel_filter grpc_connected_channel_filter = {
     con_start_transport_stream_op,
     con_start_transport_op,
@@ -128,6 +133,7 @@
     sizeof(channel_data),
     init_channel_elem,
     destroy_channel_elem,
+    con_get_peer,
     "connected",
 };
 
diff --git a/src/core/channel/http_client_filter.c b/src/core/channel/http_client_filter.c
index 6e93103..6ae8488 100644
--- a/src/core/channel/http_client_filter.c
+++ b/src/core/channel/http_client_filter.c
@@ -280,4 +280,5 @@
 const grpc_channel_filter grpc_http_client_filter = {
     hc_start_transport_op, grpc_channel_next_op, sizeof(call_data),
     init_call_elem,        destroy_call_elem,    sizeof(channel_data),
-    init_channel_elem,     destroy_channel_elem, "http-client"};
+    init_channel_elem,     destroy_channel_elem, grpc_call_next_get_peer,
+    "http-client"};
diff --git a/src/core/channel/http_server_filter.c b/src/core/channel/http_server_filter.c
index a6cbb5a..7c798d2 100644
--- a/src/core/channel/http_server_filter.c
+++ b/src/core/channel/http_server_filter.c
@@ -280,4 +280,5 @@
 const grpc_channel_filter grpc_http_server_filter = {
     hs_start_transport_op, grpc_channel_next_op, sizeof(call_data),
     init_call_elem,        destroy_call_elem,    sizeof(channel_data),
-    init_channel_elem,     destroy_channel_elem, "http-server"};
+    init_channel_elem,     destroy_channel_elem, grpc_call_next_get_peer,
+    "http-server"};
diff --git a/src/core/channel/noop_filter.c b/src/core/channel/noop_filter.c
index 5117723..d631885 100644
--- a/src/core/channel/noop_filter.c
+++ b/src/core/channel/noop_filter.c
@@ -127,4 +127,5 @@
                                                sizeof(channel_data),
                                                init_channel_elem,
                                                destroy_channel_elem,
+                                               grpc_call_next_get_peer,
                                                "no-op"};
diff --git a/src/core/client_config/README.md b/src/core/client_config/README.md
index d0700cf..fff7a5a 100644
--- a/src/core/client_config/README.md
+++ b/src/core/client_config/README.md
@@ -60,3 +60,7 @@
                    sockets - the authority must be empty, and the path
                    represents the absolute or relative path to the desired
                    socket
+
+ipv4:host:port   - a pre-resolved ipv4 dotted decimal address/port combination
+
+ipv6:[host]:port - a pre-resolved ipv6 address/port combination
diff --git a/src/core/client_config/resolvers/sockaddr_resolver.c b/src/core/client_config/resolvers/sockaddr_resolver.c
new file mode 100644
index 0000000..74584e7
--- /dev/null
+++ b/src/core/client_config/resolvers/sockaddr_resolver.c
@@ -0,0 +1,299 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/client_config/resolvers/sockaddr_resolver.h"
+
+#include <stdio.h>
+#include <string.h>
+#ifdef GPR_POSIX_SOCKET
+#include <sys/un.h>
+#endif
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/client_config/lb_policies/pick_first.h"
+#include "src/core/iomgr/resolve_address.h"
+#include "src/core/support/string.h"
+
+typedef struct {
+  /** base class: must be first */
+  grpc_resolver base;
+  /** refcount */
+  gpr_refcount refs;
+  /** subchannel factory */
+  grpc_subchannel_factory *subchannel_factory;
+  /** load balancing policy factory */
+  grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels,
+                                       size_t num_subchannels);
+
+  /** the address that we've 'resolved' */
+  struct sockaddr_storage addr;
+  int addr_len;
+
+  /** mutex guarding the rest of the state */
+  gpr_mu mu;
+  /** have we published? */
+  int published;
+  /** pending next completion, or NULL */
+  grpc_iomgr_closure *next_completion;
+  /** target config address for next completion */
+  grpc_client_config **target_config;
+} sockaddr_resolver;
+
+static void sockaddr_destroy(grpc_resolver *r);
+
+static void sockaddr_maybe_finish_next_locked(sockaddr_resolver *r);
+
+static void sockaddr_shutdown(grpc_resolver *r);
+static void sockaddr_channel_saw_error(grpc_resolver *r,
+                                       struct sockaddr *failing_address,
+                                       int failing_address_len);
+static void sockaddr_next(grpc_resolver *r, grpc_client_config **target_config,
+                          grpc_iomgr_closure *on_complete);
+
+static const grpc_resolver_vtable sockaddr_resolver_vtable = {
+    sockaddr_destroy, sockaddr_shutdown, sockaddr_channel_saw_error,
+    sockaddr_next};
+
+static void sockaddr_shutdown(grpc_resolver *resolver) {
+  sockaddr_resolver *r = (sockaddr_resolver *)resolver;
+  gpr_mu_lock(&r->mu);
+  if (r->next_completion != NULL) {
+    *r->target_config = NULL;
+    /* TODO(ctiller): add delayed callback */
+    grpc_iomgr_add_callback(r->next_completion);
+    r->next_completion = NULL;
+  }
+  gpr_mu_unlock(&r->mu);
+}
+
+static void sockaddr_channel_saw_error(grpc_resolver *resolver,
+                                       struct sockaddr *sa, int len) {}
+
+static void sockaddr_next(grpc_resolver *resolver,
+                          grpc_client_config **target_config,
+                          grpc_iomgr_closure *on_complete) {
+  sockaddr_resolver *r = (sockaddr_resolver *)resolver;
+  gpr_mu_lock(&r->mu);
+  GPR_ASSERT(!r->next_completion);
+  r->next_completion = on_complete;
+  r->target_config = target_config;
+  sockaddr_maybe_finish_next_locked(r);
+  gpr_mu_unlock(&r->mu);
+}
+
+static void sockaddr_maybe_finish_next_locked(sockaddr_resolver *r) {
+  grpc_client_config *cfg;
+  grpc_lb_policy *lb_policy;
+  grpc_subchannel *subchannel;
+  grpc_subchannel_args args;
+
+  if (r->next_completion != NULL && !r->published) {
+    cfg = grpc_client_config_create();
+    memset(&args, 0, sizeof(args));
+    args.addr = (struct sockaddr *)&r->addr;
+    args.addr_len = r->addr_len;
+    subchannel =
+        grpc_subchannel_factory_create_subchannel(r->subchannel_factory, &args);
+    lb_policy = r->lb_policy_factory(&subchannel, 1);
+    grpc_client_config_set_lb_policy(cfg, lb_policy);
+    GRPC_LB_POLICY_UNREF(lb_policy, "unix");
+    r->published = 1;
+    *r->target_config = cfg;
+    grpc_iomgr_add_callback(r->next_completion);
+    r->next_completion = NULL;
+  }
+}
+
+static void sockaddr_destroy(grpc_resolver *gr) {
+  sockaddr_resolver *r = (sockaddr_resolver *)gr;
+  gpr_mu_destroy(&r->mu);
+  grpc_subchannel_factory_unref(r->subchannel_factory);
+  gpr_free(r);
+}
+
+#ifdef GPR_POSIX_SOCKET
+static int parse_unix(grpc_uri *uri, struct sockaddr_storage *addr, int *len) {
+  struct sockaddr_un *un = (struct sockaddr_un *)addr;
+
+  un->sun_family = AF_UNIX;
+  strcpy(un->sun_path, uri->path);
+  *len = strlen(un->sun_path) + sizeof(un->sun_family) + 1;
+
+  return 1;
+}
+#endif
+
+static int parse_ipv4(grpc_uri *uri, struct sockaddr_storage *addr, int *len) {
+  const char *host_port = uri->path;
+  char *host;
+  char *port;
+  int port_num;
+  int result = 0;
+  struct sockaddr_in *in = (struct sockaddr_in *)addr;
+
+  if (*host_port == '/') ++host_port;
+  if (!gpr_split_host_port(host_port, &host, &port)) {
+    return 0;
+  }
+
+  memset(in, 0, sizeof(*in));
+  *len = sizeof(*in);
+  in->sin_family = AF_INET;
+  if (inet_pton(AF_INET, host, &in->sin_addr) == 0) {
+    gpr_log(GPR_ERROR, "invalid ipv4 address: '%s'", host);
+    goto done;
+  }
+
+  if (port != NULL) {
+    if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 ||
+        port_num > 65535) {
+      gpr_log(GPR_ERROR, "invalid ipv4 port: '%s'", port);
+      goto done;
+    }
+    in->sin_port = htons(port_num);
+  } else {
+    gpr_log(GPR_ERROR, "no port given for ipv4 scheme");
+    goto done;
+  }
+
+  result = 1;
+done:
+  gpr_free(host);
+  gpr_free(port);
+  return result;
+}
+
+static int parse_ipv6(grpc_uri *uri, struct sockaddr_storage *addr, int *len) {
+  const char *host_port = uri->path;
+  char *host;
+  char *port;
+  int port_num;
+  int result = 0;
+  struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr;
+
+  if (*host_port == '/') ++host_port;
+  if (!gpr_split_host_port(host_port, &host, &port)) {
+    return 0;
+  }
+
+  memset(in6, 0, sizeof(*in6));
+  *len = sizeof(*in6);
+  in6->sin6_family = AF_INET6;
+  if (inet_pton(AF_INET6, host, &in6->sin6_addr) == 0) {
+    gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host);
+    goto done;
+  }
+
+  if (port != NULL) {
+    if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 ||
+        port_num > 65535) {
+      gpr_log(GPR_ERROR, "invalid ipv6 port: '%s'", port);
+      goto done;
+    }
+    in6->sin6_port = htons(port_num);
+  } else {
+    gpr_log(GPR_ERROR, "no port given for ipv6 scheme");
+    goto done;
+  }
+
+  result = 1;
+done:
+  gpr_free(host);
+  gpr_free(port);
+  return result;
+}
+
+static grpc_resolver *sockaddr_create(
+    grpc_uri *uri,
+    grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels,
+                                         size_t num_subchannels),
+    grpc_subchannel_factory *subchannel_factory,
+    int parse(grpc_uri *uri, struct sockaddr_storage *dst, int *len)) {
+  sockaddr_resolver *r;
+
+  if (0 != strcmp(uri->authority, "")) {
+    gpr_log(GPR_ERROR, "authority based uri's not supported");
+    return NULL;
+  }
+
+  r = gpr_malloc(sizeof(sockaddr_resolver));
+  memset(r, 0, sizeof(*r));
+  if (!parse(uri, &r->addr, &r->addr_len)) {
+    gpr_free(r);
+    return NULL;
+  }
+
+  gpr_ref_init(&r->refs, 1);
+  gpr_mu_init(&r->mu);
+  grpc_resolver_init(&r->base, &sockaddr_resolver_vtable);
+  r->subchannel_factory = subchannel_factory;
+  r->lb_policy_factory = lb_policy_factory;
+
+  grpc_subchannel_factory_ref(subchannel_factory);
+  return &r->base;
+}
+
+/*
+ * FACTORY
+ */
+
+static void sockaddr_factory_ref(grpc_resolver_factory *factory) {}
+
+static void sockaddr_factory_unref(grpc_resolver_factory *factory) {}
+
+#define DECL_FACTORY(name)                                            \
+  static grpc_resolver *name##_factory_create_resolver(               \
+      grpc_resolver_factory *factory, grpc_uri *uri,                  \
+      grpc_subchannel_factory *subchannel_factory) {                  \
+    return sockaddr_create(uri, grpc_create_pick_first_lb_policy,     \
+                           subchannel_factory, parse_##name);         \
+  }                                                                   \
+  static const grpc_resolver_factory_vtable name##_factory_vtable = { \
+      sockaddr_factory_ref, sockaddr_factory_unref,                   \
+      name##_factory_create_resolver};                                \
+  static grpc_resolver_factory name##_resolver_factory = {            \
+      &name##_factory_vtable};                                        \
+  grpc_resolver_factory *grpc_##name##_resolver_factory_create() {    \
+    return &name##_resolver_factory;                                  \
+  }
+
+#ifdef GPR_POSIX_SOCKET
+DECL_FACTORY(unix)
+#endif
+DECL_FACTORY(ipv4)
+DECL_FACTORY(ipv6)
diff --git a/src/core/client_config/resolvers/unix_resolver_posix.h b/src/core/client_config/resolvers/sockaddr_resolver.h
similarity index 92%
rename from src/core/client_config/resolvers/unix_resolver_posix.h
rename to src/core/client_config/resolvers/sockaddr_resolver.h
index 57ace59..1b7a18f 100644
--- a/src/core/client_config/resolvers/unix_resolver_posix.h
+++ b/src/core/client_config/resolvers/sockaddr_resolver.h
@@ -38,7 +38,13 @@
 
 #include "src/core/client_config/resolver_factory.h"
 
+grpc_resolver_factory *grpc_ipv4_resolver_factory_create(void);
+
+grpc_resolver_factory *grpc_ipv6_resolver_factory_create(void);
+
+#ifdef GPR_POSIX_SOCKET
 /** Create a unix resolver factory */
 grpc_resolver_factory *grpc_unix_resolver_factory_create(void);
+#endif
 
 #endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H */
diff --git a/src/core/client_config/resolvers/unix_resolver_posix.c b/src/core/client_config/resolvers/unix_resolver_posix.c
deleted file mode 100644
index be515d2..0000000
--- a/src/core/client_config/resolvers/unix_resolver_posix.c
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <grpc/support/port_platform.h>
-#ifdef GPR_POSIX_SOCKET
-
-#include "src/core/client_config/resolvers/unix_resolver_posix.h"
-
-#include <string.h>
-#include <sys/un.h>
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/string_util.h>
-
-#include "src/core/client_config/lb_policies/pick_first.h"
-#include "src/core/iomgr/resolve_address.h"
-#include "src/core/support/string.h"
-
-typedef struct {
-  /** base class: must be first */
-  grpc_resolver base;
-  /** refcount */
-  gpr_refcount refs;
-  /** subchannel factory */
-  grpc_subchannel_factory *subchannel_factory;
-  /** load balancing policy factory */
-  grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels,
-                                       size_t num_subchannels);
-
-  /** the address that we've 'resolved' */
-  struct sockaddr_un addr;
-  int addr_len;
-
-  /** mutex guarding the rest of the state */
-  gpr_mu mu;
-  /** have we published? */
-  int published;
-  /** pending next completion, or NULL */
-  grpc_iomgr_closure *next_completion;
-  /** target config address for next completion */
-  grpc_client_config **target_config;
-} unix_resolver;
-
-static void unix_destroy(grpc_resolver *r);
-
-static void unix_maybe_finish_next_locked(unix_resolver *r);
-
-static void unix_shutdown(grpc_resolver *r);
-static void unix_channel_saw_error(grpc_resolver *r,
-                                   struct sockaddr *failing_address,
-                                   int failing_address_len);
-static void unix_next(grpc_resolver *r, grpc_client_config **target_config,
-                      grpc_iomgr_closure *on_complete);
-
-static const grpc_resolver_vtable unix_resolver_vtable = {
-    unix_destroy, unix_shutdown, unix_channel_saw_error, unix_next};
-
-static void unix_shutdown(grpc_resolver *resolver) {
-  unix_resolver *r = (unix_resolver *)resolver;
-  gpr_mu_lock(&r->mu);
-  if (r->next_completion != NULL) {
-    *r->target_config = NULL;
-    /* TODO(ctiller): add delayed callback */
-    grpc_iomgr_add_callback(r->next_completion);
-    r->next_completion = NULL;
-  }
-  gpr_mu_unlock(&r->mu);
-}
-
-static void unix_channel_saw_error(grpc_resolver *resolver, struct sockaddr *sa,
-                                   int len) {}
-
-static void unix_next(grpc_resolver *resolver,
-                      grpc_client_config **target_config,
-                      grpc_iomgr_closure *on_complete) {
-  unix_resolver *r = (unix_resolver *)resolver;
-  gpr_mu_lock(&r->mu);
-  GPR_ASSERT(!r->next_completion);
-  r->next_completion = on_complete;
-  r->target_config = target_config;
-  unix_maybe_finish_next_locked(r);
-  gpr_mu_unlock(&r->mu);
-}
-
-static void unix_maybe_finish_next_locked(unix_resolver *r) {
-  grpc_client_config *cfg;
-  grpc_lb_policy *lb_policy;
-  grpc_subchannel *subchannel;
-  grpc_subchannel_args args;
-
-  if (r->next_completion != NULL && !r->published) {
-    cfg = grpc_client_config_create();
-    memset(&args, 0, sizeof(args));
-    args.addr = (struct sockaddr *)&r->addr;
-    args.addr_len = r->addr_len;
-    subchannel =
-        grpc_subchannel_factory_create_subchannel(r->subchannel_factory, &args);
-    lb_policy = r->lb_policy_factory(&subchannel, 1);
-    grpc_client_config_set_lb_policy(cfg, lb_policy);
-    GRPC_LB_POLICY_UNREF(lb_policy, "unix");
-    r->published = 1;
-    *r->target_config = cfg;
-    grpc_iomgr_add_callback(r->next_completion);
-    r->next_completion = NULL;
-  }
-}
-
-static void unix_destroy(grpc_resolver *gr) {
-  unix_resolver *r = (unix_resolver *)gr;
-  gpr_mu_destroy(&r->mu);
-  grpc_subchannel_factory_unref(r->subchannel_factory);
-  gpr_free(r);
-}
-
-static grpc_resolver *unix_create(
-    grpc_uri *uri,
-    grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels,
-                                         size_t num_subchannels),
-    grpc_subchannel_factory *subchannel_factory) {
-  unix_resolver *r;
-
-  if (0 != strcmp(uri->authority, "")) {
-    gpr_log(GPR_ERROR, "authority based uri's not supported");
-    return NULL;
-  }
-
-  r = gpr_malloc(sizeof(unix_resolver));
-  memset(r, 0, sizeof(*r));
-  gpr_ref_init(&r->refs, 1);
-  gpr_mu_init(&r->mu);
-  grpc_resolver_init(&r->base, &unix_resolver_vtable);
-  r->subchannel_factory = subchannel_factory;
-  r->lb_policy_factory = lb_policy_factory;
-
-  r->addr.sun_family = AF_UNIX;
-  strcpy(r->addr.sun_path, uri->path);
-  r->addr_len = strlen(r->addr.sun_path) + sizeof(r->addr.sun_family) + 1;
-
-  grpc_subchannel_factory_ref(subchannel_factory);
-  return &r->base;
-}
-
-/*
- * FACTORY
- */
-
-static void unix_factory_ref(grpc_resolver_factory *factory) {}
-
-static void unix_factory_unref(grpc_resolver_factory *factory) {}
-
-static grpc_resolver *unix_factory_create_resolver(
-    grpc_resolver_factory *factory, grpc_uri *uri,
-    grpc_subchannel_factory *subchannel_factory) {
-  return unix_create(uri, grpc_create_pick_first_lb_policy, subchannel_factory);
-}
-
-static const grpc_resolver_factory_vtable unix_factory_vtable = {
-    unix_factory_ref, unix_factory_unref, unix_factory_create_resolver};
-static grpc_resolver_factory unix_resolver_factory = {&unix_factory_vtable};
-
-grpc_resolver_factory *grpc_unix_resolver_factory_create() {
-  return &unix_resolver_factory;
-}
-
-#endif
diff --git a/src/core/client_config/subchannel.c b/src/core/client_config/subchannel.c
index 487f5af..358b907 100644
--- a/src/core/client_config/subchannel.c
+++ b/src/core/client_config/subchannel.c
@@ -640,6 +640,12 @@
   }
 }
 
+char *grpc_subchannel_call_get_peer(grpc_subchannel_call *call) {
+  grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call);
+  grpc_call_element *top_elem = grpc_call_stack_element(call_stack, 0);
+  return top_elem->filter->get_peer(top_elem);
+}
+
 void grpc_subchannel_call_process_op(grpc_subchannel_call *call,
                                      grpc_transport_stream_op *op) {
   grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call);
diff --git a/src/core/client_config/subchannel.h b/src/core/client_config/subchannel.h
index a23a623..d1cd33b 100644
--- a/src/core/client_config/subchannel.h
+++ b/src/core/client_config/subchannel.h
@@ -100,6 +100,9 @@
 void grpc_subchannel_call_process_op(grpc_subchannel_call *subchannel_call,
                                      grpc_transport_stream_op *op);
 
+/** continue querying for peer */
+char *grpc_subchannel_call_get_peer(grpc_subchannel_call *subchannel_call);
+
 struct grpc_subchannel_args {
   /** Channel filters for this channel - wrapped factories will likely
       want to mutate this */
diff --git a/src/core/iomgr/endpoint.c b/src/core/iomgr/endpoint.c
index 9648795..f2b44aa 100644
--- a/src/core/iomgr/endpoint.c
+++ b/src/core/iomgr/endpoint.c
@@ -53,3 +53,7 @@
 void grpc_endpoint_shutdown(grpc_endpoint *ep) { ep->vtable->shutdown(ep); }
 
 void grpc_endpoint_destroy(grpc_endpoint *ep) { ep->vtable->destroy(ep); }
+
+char *grpc_endpoint_get_peer(grpc_endpoint *ep) {
+  return ep->vtable->get_peer(ep);
+}
diff --git a/src/core/iomgr/endpoint.h b/src/core/iomgr/endpoint.h
index 881e851..ee0becf 100644
--- a/src/core/iomgr/endpoint.h
+++ b/src/core/iomgr/endpoint.h
@@ -72,12 +72,15 @@
   void (*add_to_pollset)(grpc_endpoint *ep, grpc_pollset *pollset);
   void (*shutdown)(grpc_endpoint *ep);
   void (*destroy)(grpc_endpoint *ep);
+  char *(*get_peer)(grpc_endpoint *ep);
 };
 
 /* When data is available on the connection, calls the callback with slices. */
 void grpc_endpoint_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb,
                                   void *user_data);
 
+char *grpc_endpoint_get_peer(grpc_endpoint *ep);
+
 /* Write slices out to the socket.
 
    If the connection is ready for more data after the end of the call, it
diff --git a/src/core/iomgr/endpoint_pair_posix.c b/src/core/iomgr/endpoint_pair_posix.c
index fa2d255..deae9c6 100644
--- a/src/core/iomgr/endpoint_pair_posix.c
+++ b/src/core/iomgr/endpoint_pair_posix.c
@@ -66,12 +66,12 @@
   create_sockets(sv);
 
   gpr_asprintf(&final_name, "%s:client", name);
-  p.client =
-      grpc_tcp_create(grpc_fd_create(sv[1], final_name), read_slice_size);
+  p.client = grpc_tcp_create(grpc_fd_create(sv[1], final_name), read_slice_size,
+                             "socketpair-server");
   gpr_free(final_name);
   gpr_asprintf(&final_name, "%s:server", name);
-  p.server =
-      grpc_tcp_create(grpc_fd_create(sv[0], final_name), read_slice_size);
+  p.server = grpc_tcp_create(grpc_fd_create(sv[0], final_name), read_slice_size,
+                             "socketpair-client");
   gpr_free(final_name);
   return p;
 }
diff --git a/src/core/iomgr/endpoint_pair_windows.c b/src/core/iomgr/endpoint_pair_windows.c
index c6790b2..7d81470 100644
--- a/src/core/iomgr/endpoint_pair_windows.c
+++ b/src/core/iomgr/endpoint_pair_windows.c
@@ -81,8 +81,8 @@
   SOCKET sv[2];
   grpc_endpoint_pair p;
   create_sockets(sv);
-  p.client = grpc_tcp_create(grpc_winsocket_create(sv[1], "endpoint:client"));
-  p.server = grpc_tcp_create(grpc_winsocket_create(sv[0], "endpoint:server"));
+  p.client = grpc_tcp_create(grpc_winsocket_create(sv[1], "endpoint:client"), "endpoint:server");
+  p.server = grpc_tcp_create(grpc_winsocket_create(sv[0], "endpoint:server"), "endpoint:client");
   return p;
 }
 
diff --git a/src/core/iomgr/sockaddr_utils.c b/src/core/iomgr/sockaddr_utils.c
index e91b94f..71ac12e 100644
--- a/src/core/iomgr/sockaddr_utils.c
+++ b/src/core/iomgr/sockaddr_utils.c
@@ -36,12 +36,18 @@
 #include <errno.h>
 #include <string.h>
 
-#include "src/core/support/string.h"
+#ifdef GPR_POSIX_SOCKET
+#include <sys/un.h>
+#endif
+
+#include <grpc/support/alloc.h>
 #include <grpc/support/host_port.h>
 #include <grpc/support/log.h>
 #include <grpc/support/port_platform.h>
 #include <grpc/support/string_util.h>
 
+#include "src/core/support/string.h"
+
 static const gpr_uint8 kV4MappedPrefix[] = {0, 0, 0, 0, 0,    0,
                                             0, 0, 0, 0, 0xff, 0xff};
 
@@ -161,6 +167,31 @@
   return ret;
 }
 
+char *grpc_sockaddr_to_uri(const struct sockaddr *addr) {
+  char *temp;
+  char *result;
+
+  switch (addr->sa_family) {
+    case AF_INET:
+      grpc_sockaddr_to_string(&temp, addr, 0);
+      gpr_asprintf(&result, "ipv4:%s", temp);
+      gpr_free(temp);
+      return result;
+    case AF_INET6:
+      grpc_sockaddr_to_string(&temp, addr, 0);
+      gpr_asprintf(&result, "ipv6:%s", temp);
+      gpr_free(temp);
+      return result;
+#ifdef GPR_POSIX_SOCKET
+    case AF_UNIX:
+      gpr_asprintf(&result, "unix:%s", ((struct sockaddr_un *)addr)->sun_path);
+      return result;
+#endif
+  }
+
+  return NULL;
+}
+
 int grpc_sockaddr_get_port(const struct sockaddr *addr) {
   switch (addr->sa_family) {
     case AF_INET:
diff --git a/src/core/iomgr/sockaddr_utils.h b/src/core/iomgr/sockaddr_utils.h
index bdfb834..99f1ed5 100644
--- a/src/core/iomgr/sockaddr_utils.h
+++ b/src/core/iomgr/sockaddr_utils.h
@@ -84,4 +84,6 @@
 int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr,
                             int normalize);
 
+char *grpc_sockaddr_to_uri(const struct sockaddr *addr);
+
 #endif  /* GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_UTILS_H */
diff --git a/src/core/iomgr/tcp_client_posix.c b/src/core/iomgr/tcp_client_posix.c
index 41d8b16..392eda9 100644
--- a/src/core/iomgr/tcp_client_posix.c
+++ b/src/core/iomgr/tcp_client_posix.c
@@ -64,6 +64,7 @@
   int refs;
   grpc_iomgr_closure write_closure;
   grpc_pollset_set *interested_parties;
+  char *addr_str;
 } async_connect;
 
 static int prepare_socket(const struct sockaddr *addr, int fd) {
@@ -99,6 +100,7 @@
   gpr_mu_unlock(&ac->mu);
   if (done) {
     gpr_mu_destroy(&ac->mu);
+    gpr_free(ac->addr_str);
     gpr_free(ac);
   }
 }
@@ -158,7 +160,8 @@
       }
     } else {
       grpc_pollset_set_del_fd(ac->interested_parties, ac->fd);
-      ep = grpc_tcp_create(ac->fd, GRPC_TCP_DEFAULT_READ_SLICE_SIZE);
+      ep = grpc_tcp_create(ac->fd, GRPC_TCP_DEFAULT_READ_SLICE_SIZE,
+                           ac->addr_str);
       goto finish;
     }
   } else {
@@ -179,6 +182,7 @@
   gpr_mu_unlock(&ac->mu);
   if (done) {
     gpr_mu_destroy(&ac->mu);
+    gpr_free(ac->addr_str);
     gpr_free(ac);
   }
   cb(cb_arg, ep);
@@ -223,13 +227,13 @@
     err = connect(fd, addr, addr_len);
   } while (err < 0 && errno == EINTR);
 
-  grpc_sockaddr_to_string(&addr_str, addr, 1);
+  addr_str = grpc_sockaddr_to_uri(addr);
   gpr_asprintf(&name, "tcp-client:%s", addr_str);
 
   fdobj = grpc_fd_create(fd, name);
 
   if (err >= 0) {
-    cb(arg, grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE));
+    cb(arg, grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str));
     goto done;
   }
 
@@ -247,6 +251,8 @@
   ac->cb_arg = arg;
   ac->fd = fdobj;
   ac->interested_parties = interested_parties;
+  ac->addr_str = addr_str;
+  addr_str = NULL;
   gpr_mu_init(&ac->mu);
   ac->refs = 2;
   ac->write_closure.cb = on_writable;
diff --git a/src/core/iomgr/tcp_client_windows.c b/src/core/iomgr/tcp_client_windows.c
index 39fd431..79a58fe 100644
--- a/src/core/iomgr/tcp_client_windows.c
+++ b/src/core/iomgr/tcp_client_windows.c
@@ -58,6 +58,7 @@
   grpc_winsocket *socket;
   gpr_timespec deadline;
   grpc_alarm alarm;
+  char *addr_name;
   int refs;
   int aborted;
 } async_connect;
@@ -67,6 +68,7 @@
   gpr_mu_unlock(&ac->mu);
   if (done) {
     gpr_mu_destroy(&ac->mu);
+    gpr_free(ac->addr_name);
     gpr_free(ac);
   }
 }
@@ -107,7 +109,7 @@
       gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message);
       gpr_free(utf8_message);
     } else if (!aborted) {
-      ep = grpc_tcp_create(ac->socket);
+      ep = grpc_tcp_create(ac->socket, ac->addr_name);
     }
   } else {
     gpr_log(GPR_ERROR, "on_connect is shutting down");
@@ -213,6 +215,7 @@
   ac->socket = socket;
   gpr_mu_init(&ac->mu);
   ac->refs = 2;
+  ac->addr_name = grpc_sockaddr_to_uri(addr);
   ac->aborted = 0;
 
   grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac,
diff --git a/src/core/iomgr/tcp_posix.c b/src/core/iomgr/tcp_posix.c
index b6d6efc..1e8432d 100644
--- a/src/core/iomgr/tcp_posix.c
+++ b/src/core/iomgr/tcp_posix.c
@@ -44,15 +44,17 @@
 #include <sys/socket.h>
 #include <unistd.h>
 
-#include "src/core/support/string.h"
-#include "src/core/debug/trace.h"
-#include "src/core/profiling/timers.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/slice.h>
+#include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 
+#include "src/core/support/string.h"
+#include "src/core/debug/trace.h"
+#include "src/core/profiling/timers.h"
+
 #ifdef GPR_HAVE_MSG_NOSIGNAL
 #define SENDMSG_FLAGS MSG_NOSIGNAL
 #else
@@ -282,6 +284,8 @@
   grpc_iomgr_closure write_closure;
 
   grpc_iomgr_closure handle_read_closure;
+
+  char *peer_string;
 } grpc_tcp;
 
 static void grpc_tcp_handle_read(void *arg /* grpc_tcp */, int success);
@@ -296,6 +300,7 @@
   int refcount_zero = gpr_unref(&tcp->refcount);
   if (refcount_zero) {
     grpc_fd_orphan(tcp->em_fd, NULL, "tcp_unref_orphan");
+    gpr_free(tcp->peer_string);
     gpr_free(tcp);
   }
 }
@@ -567,13 +572,20 @@
   grpc_pollset_add_fd(pollset, tcp->em_fd);
 }
 
-static const grpc_endpoint_vtable vtable = {
-    grpc_tcp_notify_on_read, grpc_tcp_write, grpc_tcp_add_to_pollset,
-    grpc_tcp_shutdown, grpc_tcp_destroy};
+static char *grpc_tcp_get_peer(grpc_endpoint *ep) {
+  grpc_tcp *tcp = (grpc_tcp *)ep;
+  return gpr_strdup(tcp->peer_string);
+}
 
-grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size) {
+static const grpc_endpoint_vtable vtable = {
+    grpc_tcp_notify_on_read, grpc_tcp_write,   grpc_tcp_add_to_pollset,
+    grpc_tcp_shutdown,       grpc_tcp_destroy, grpc_tcp_get_peer};
+
+grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size,
+                               const char *peer_string) {
   grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp));
   tcp->base.vtable = &vtable;
+  tcp->peer_string = gpr_strdup(peer_string);
   tcp->fd = em_fd->fd;
   tcp->read_cb = NULL;
   tcp->write_cb = NULL;
diff --git a/src/core/iomgr/tcp_posix.h b/src/core/iomgr/tcp_posix.h
index 44279d5..d752fea 100644
--- a/src/core/iomgr/tcp_posix.h
+++ b/src/core/iomgr/tcp_posix.h
@@ -53,6 +53,7 @@
 
 /* Create a tcp endpoint given a file desciptor and a read slice size.
    Takes ownership of fd. */
-grpc_endpoint *grpc_tcp_create(grpc_fd *fd, size_t read_slice_size);
+grpc_endpoint *grpc_tcp_create(grpc_fd *fd, size_t read_slice_size,
+                               const char *peer_string);
 
 #endif  /* GRPC_INTERNAL_CORE_IOMGR_TCP_POSIX_H */
diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c
index 5854031..8538600 100644
--- a/src/core/iomgr/tcp_server_posix.c
+++ b/src/core/iomgr/tcp_server_posix.c
@@ -332,7 +332,7 @@
 
     grpc_set_socket_no_sigpipe_if_possible(fd);
 
-    grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1);
+    addr_str = grpc_sockaddr_to_uri((struct sockaddr *)&addr);
     gpr_asprintf(&name, "tcp-server-connection:%s", addr_str);
 
     fdobj = grpc_fd_create(fd, name);
@@ -342,8 +342,9 @@
     for (i = 0; i < sp->server->pollset_count; i++) {
       grpc_pollset_add_fd(sp->server->pollsets[i], fdobj);
     }
-    sp->server->cb(sp->server->cb_arg,
-                   grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE));
+    sp->server->cb(
+        sp->server->cb_arg,
+        grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str));
 
     gpr_free(name);
     gpr_free(addr_str);
diff --git a/src/core/iomgr/tcp_server_windows.c b/src/core/iomgr/tcp_server_windows.c
index 187009b..8f634fc 100644
--- a/src/core/iomgr/tcp_server_windows.c
+++ b/src/core/iomgr/tcp_server_windows.c
@@ -243,6 +243,10 @@
   SOCKET sock = sp->new_socket;
   grpc_winsocket_callback_info *info = &sp->socket->read_info;
   grpc_endpoint *ep = NULL;
+  struct sockaddr_storage peer_name;
+  char *peer_name_string;
+  char *fd_name;
+  int peer_name_len = sizeof(peer_name);
   DWORD transfered_bytes;
   DWORD flags;
   BOOL wsa_success;
@@ -277,8 +281,12 @@
     }
   } else {
     if (!sp->shutting_down) {
-      /* TODO(ctiller): add sockaddr address to label */
-      ep = grpc_tcp_create(grpc_winsocket_create(sock, "server"));
+      getpeername(sock, (struct sockaddr*)&peer_name, &peer_name_len);
+      peer_name_string = grpc_sockaddr_to_uri((struct sockaddr*)&peer_name);
+      gpr_asprintf(&fd_name, "tcp_server:%s", peer_name_string);
+      ep = grpc_tcp_create(grpc_winsocket_create(sock, fd_name), peer_name_string);
+      gpr_free(fd_name);
+      gpr_free(peer_name_string);
     }
   }
 
diff --git a/src/core/iomgr/tcp_windows.c b/src/core/iomgr/tcp_windows.c
index 1bf81a7..d68e6ae 100644
--- a/src/core/iomgr/tcp_windows.c
+++ b/src/core/iomgr/tcp_windows.c
@@ -96,6 +96,8 @@
      to protect ourselves when requesting a shutdown. */
   gpr_mu mu;
   int shutting_down;
+
+  char *peer_string;
 } grpc_tcp;
 
 static void tcp_ref(grpc_tcp *tcp) {
@@ -107,6 +109,7 @@
     gpr_slice_buffer_destroy(&tcp->write_slices);
     grpc_winsocket_orphan(tcp->socket);
     gpr_mu_destroy(&tcp->mu);
+    gpr_free(tcp->peer_string);
     gpr_free(tcp);
   }
 }
@@ -393,11 +396,16 @@
   tcp_unref(tcp);
 }
 
+static char *win_get_peer(grpc_endpoint *ep) {
+  grpc_tcp *tcp = (grpc_tcp *)ep;
+  return gpr_strdup(tcp->peer_string);
+}
+
 static grpc_endpoint_vtable vtable = {
-  win_notify_on_read, win_write, win_add_to_pollset, win_shutdown, win_destroy
+  win_notify_on_read, win_write, win_add_to_pollset, win_shutdown, win_destroy, win_get_peer
 };
 
-grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket) {
+grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string) {
   grpc_tcp *tcp = (grpc_tcp *) gpr_malloc(sizeof(grpc_tcp));
   memset(tcp, 0, sizeof(grpc_tcp));
   tcp->base.vtable = &vtable;
@@ -405,6 +413,7 @@
   gpr_mu_init(&tcp->mu);
   gpr_slice_buffer_init(&tcp->write_slices);
   gpr_ref_init(&tcp->refcount, 1);
+  tcp->peer_string = gpr_strdup(peer_string);
   return &tcp->base;
 }
 
diff --git a/src/core/iomgr/tcp_windows.h b/src/core/iomgr/tcp_windows.h
index 4cbc12c..7e301db 100644
--- a/src/core/iomgr/tcp_windows.h
+++ b/src/core/iomgr/tcp_windows.h
@@ -50,7 +50,7 @@
 /* Create a tcp endpoint given a winsock handle.
  * Takes ownership of the handle.
  */
-grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket);
+grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string);
 
 int grpc_tcp_prepare_socket(SOCKET sock);
 
diff --git a/src/core/security/client_auth_filter.c b/src/core/security/client_auth_filter.c
index 9e49a80..9a69f53 100644
--- a/src/core/security/client_auth_filter.c
+++ b/src/core/security/client_auth_filter.c
@@ -344,6 +344,8 @@
 }
 
 const grpc_channel_filter grpc_client_auth_filter = {
-    auth_start_transport_op, grpc_channel_next_op, sizeof(call_data),
-    init_call_elem,          destroy_call_elem,    sizeof(channel_data),
-    init_channel_elem,       destroy_channel_elem, "client-auth"};
+    auth_start_transport_op, grpc_channel_next_op,
+    sizeof(call_data),       init_call_elem,
+    destroy_call_elem,       sizeof(channel_data),
+    init_channel_elem,       destroy_channel_elem,
+    grpc_call_next_get_peer, "client-auth"};
diff --git a/src/core/security/secure_endpoint.c b/src/core/security/secure_endpoint.c
index 3548198..e189380 100644
--- a/src/core/security/secure_endpoint.c
+++ b/src/core/security/secure_endpoint.c
@@ -331,9 +331,14 @@
   grpc_endpoint_add_to_pollset(ep->wrapped_ep, pollset);
 }
 
+static char *endpoint_get_peer(grpc_endpoint *secure_ep) {
+  secure_endpoint *ep = (secure_endpoint *)secure_ep;
+  return grpc_endpoint_get_peer(ep->wrapped_ep);
+}
+
 static const grpc_endpoint_vtable vtable = {
     endpoint_notify_on_read, endpoint_write, endpoint_add_to_pollset,
-    endpoint_shutdown, endpoint_unref};
+    endpoint_shutdown,       endpoint_unref, endpoint_get_peer};
 
 grpc_endpoint *grpc_secure_endpoint_create(
     struct tsi_frame_protector *protector, grpc_endpoint *transport,
diff --git a/src/core/security/server_auth_filter.c b/src/core/security/server_auth_filter.c
index 10eef6d..69789c2 100644
--- a/src/core/security/server_auth_filter.c
+++ b/src/core/security/server_auth_filter.c
@@ -120,6 +120,8 @@
 }
 
 const grpc_channel_filter grpc_server_auth_filter = {
-    auth_start_transport_op, grpc_channel_next_op, sizeof(call_data),
-    init_call_elem,          destroy_call_elem,    sizeof(channel_data),
-    init_channel_elem,       destroy_channel_elem, "server-auth"};
+    auth_start_transport_op, grpc_channel_next_op,
+    sizeof(call_data),       init_call_elem,
+    destroy_call_elem,       sizeof(channel_data),
+    init_channel_elem,       destroy_channel_elem,
+    grpc_call_next_get_peer, "server-auth"};
diff --git a/src/core/support/host_port.c b/src/core/support/host_port.c
index 0906ebc..a28f04d 100644
--- a/src/core/support/host_port.c
+++ b/src/core/support/host_port.c
@@ -50,7 +50,7 @@
   }
 }
 
-void gpr_split_host_port(const char *name, char **host, char **port) {
+int gpr_split_host_port(const char *name, char **host, char **port) {
   const char *host_start;
   size_t host_len;
   const char *port_start;
@@ -63,7 +63,7 @@
     const char *rbracket = strchr(name, ']');
     if (rbracket == NULL) {
       /* Unmatched [ */
-      return;
+      return 0;
     }
     if (rbracket[1] == '\0') {
       /* ]<end> */
@@ -73,14 +73,14 @@
       port_start = rbracket + 2;
     } else {
       /* ]<invalid> */
-      return;
+      return 0;
     }
     host_start = name + 1;
     host_len = (size_t)(rbracket - host_start);
     if (memchr(host_start, ':', host_len) == NULL) {
       /* Require all bracketed hosts to contain a colon, because a hostname or
       IPv4 address should never use brackets. */
-      return;
+      return 0;
     }
   } else {
     const char *colon = strchr(name, ':');
@@ -105,4 +105,6 @@
   if (port_start != NULL) {
     *port = gpr_strdup(port_start);
   }
+
+  return 1;
 }
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index e08273e..aefcbad 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -1253,6 +1253,11 @@
   elem->filter->start_transport_stream_op(elem, op);
 }
 
+char *grpc_call_get_peer(grpc_call *call) {
+  grpc_call_element *elem = CALL_ELEM_FROM_CALL(call, 0);
+  return elem->filter->get_peer(elem);
+}
+
 grpc_call *grpc_call_from_top_element(grpc_call_element *elem) {
   return CALL_FROM_TOP_ELEM(elem);
 }
diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c
index a6438ff..4052c65 100644
--- a/src/core/surface/channel.c
+++ b/src/core/surface/channel.c
@@ -36,12 +36,14 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
 #include "src/core/iomgr/iomgr.h"
 #include "src/core/support/string.h"
 #include "src/core/surface/call.h"
 #include "src/core/surface/init.h"
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
 
 /** Cache grpc-status: X mdelems for X = 0..NUM_CACHED_STATUS_ELEMS.
  *  Avoids needing to take a metadata context lock for sending status
@@ -73,6 +75,7 @@
   gpr_mu registered_call_mu;
   registered_call *registered_calls;
   grpc_iomgr_closure destroy_closure;
+  char *target;
 };
 
 #define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c) + 1))
@@ -85,13 +88,14 @@
 #define DEFAULT_MAX_MESSAGE_LENGTH (100 * 1024 * 1024)
 
 grpc_channel *grpc_channel_create_from_filters(
-    const grpc_channel_filter **filters, size_t num_filters,
+    const char *target, const grpc_channel_filter **filters, size_t num_filters,
     const grpc_channel_args *args, grpc_mdctx *mdctx, int is_client) {
   size_t i;
   size_t size =
       sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters);
   grpc_channel *channel = gpr_malloc(size);
   memset(channel, 0, sizeof(*channel));
+  channel->target = gpr_strdup(target);
   GPR_ASSERT(grpc_is_initialized() && "call grpc_init()");
   channel->is_client = is_client;
   /* decremented by grpc_channel_destroy */
@@ -137,6 +141,10 @@
   return channel;
 }
 
+char *grpc_channel_get_target(grpc_channel *channel) {
+  return gpr_strdup(channel->target);
+}
+
 static grpc_call *grpc_channel_create_call_internal(
     grpc_channel *channel, grpc_completion_queue *cq, grpc_mdelem *path_mdelem,
     grpc_mdelem *authority_mdelem, gpr_timespec deadline) {
@@ -222,6 +230,7 @@
   }
   grpc_mdctx_unref(channel->metadata_context);
   gpr_mu_destroy(&channel->registered_call_mu);
+  gpr_free(channel->target);
   gpr_free(channel);
 }
 
diff --git a/src/core/surface/channel.h b/src/core/surface/channel.h
index 4e03eb4..9e0646e 100644
--- a/src/core/surface/channel.h
+++ b/src/core/surface/channel.h
@@ -38,7 +38,7 @@
 #include "src/core/client_config/subchannel_factory.h"
 
 grpc_channel *grpc_channel_create_from_filters(
-    const grpc_channel_filter **filters, size_t count,
+    const char *target, const grpc_channel_filter **filters, size_t count,
     const grpc_channel_args *args, grpc_mdctx *mdctx, int is_client);
 
 /** Get a (borrowed) pointer to this channels underlying channel stack */
diff --git a/src/core/surface/channel_create.c b/src/core/surface/channel_create.c
index 91c7b35..778f710 100644
--- a/src/core/surface/channel_create.c
+++ b/src/core/surface/channel_create.c
@@ -179,7 +179,8 @@
     return NULL;
   }
 
-  channel = grpc_channel_create_from_filters(filters, n, args, mdctx, 1);
+  channel =
+      grpc_channel_create_from_filters(target, filters, n, args, mdctx, 1);
   grpc_client_channel_set_resolver(grpc_channel_get_channel_stack(channel),
                                    resolver);
   GRPC_RESOLVER_UNREF(resolver, "create");
diff --git a/src/core/surface/init.c b/src/core/surface/init.c
index 04e27d3..5cba479 100644
--- a/src/core/surface/init.c
+++ b/src/core/surface/init.c
@@ -39,6 +39,7 @@
 #include "src/core/channel/channel_stack.h"
 #include "src/core/client_config/resolver_registry.h"
 #include "src/core/client_config/resolvers/dns_resolver.h"
+#include "src/core/client_config/resolvers/sockaddr_resolver.h"
 #include "src/core/debug/trace.h"
 #include "src/core/iomgr/iomgr.h"
 #include "src/core/profiling/timers.h"
@@ -47,10 +48,6 @@
 #include "src/core/surface/surface_trace.h"
 #include "src/core/transport/chttp2_transport.h"
 
-#ifdef GPR_POSIX_SOCKET
-#include "src/core/client_config/resolvers/unix_resolver_posix.h"
-#endif
-
 static gpr_once g_basic_init = GPR_ONCE_INIT;
 static gpr_mu g_init_mu;
 static int g_initializations;
@@ -68,6 +65,8 @@
     gpr_time_init();
     grpc_resolver_registry_init("dns:///");
     grpc_register_resolver_type("dns", grpc_dns_resolver_factory_create());
+    grpc_register_resolver_type("ipv4", grpc_ipv4_resolver_factory_create());
+    grpc_register_resolver_type("ipv6", grpc_ipv6_resolver_factory_create());
 #ifdef GPR_POSIX_SOCKET
     grpc_register_resolver_type("unix", grpc_unix_resolver_factory_create());
 #endif
diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c
index 3f2bb5c..c4215a2 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -47,7 +47,10 @@
   grpc_linked_mdelem details;
 } call_data;
 
-typedef struct { grpc_mdctx *mdctx; } channel_data;
+typedef struct {
+  grpc_mdctx *mdctx;
+  grpc_channel *master;
+} channel_data;
 
 static void lame_start_transport_stream_op(grpc_call_element *elem,
                                            grpc_transport_stream_op *op) {
@@ -82,6 +85,11 @@
   }
 }
 
+static char *lame_get_peer(grpc_call_element *elem) {
+  channel_data *chand = elem->channel_data;
+  return grpc_channel_get_target(chand->master);
+}
+
 static void lame_start_transport_op(grpc_channel_element *elem,
                                     grpc_transport_op *op) {
   if (op->on_connectivity_state_change) {
@@ -112,6 +120,7 @@
   GPR_ASSERT(is_first);
   GPR_ASSERT(is_last);
   chand->mdctx = mdctx;
+  chand->master = master;
 }
 
 static void destroy_channel_elem(grpc_channel_element *elem) {}
@@ -125,11 +134,12 @@
     sizeof(channel_data),
     init_channel_elem,
     destroy_channel_elem,
+    lame_get_peer,
     "lame-client",
 };
 
-grpc_channel *grpc_lame_client_channel_create(void) {
+grpc_channel *grpc_lame_client_channel_create(const char *target) {
   static const grpc_channel_filter *filters[] = {&lame_filter};
-  return grpc_channel_create_from_filters(filters, 1, NULL, grpc_mdctx_create(),
-                                          1);
+  return grpc_channel_create_from_filters(target, filters, 1, NULL,
+                                          grpc_mdctx_create(), 1);
 }
diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c
index d87ec97..a280311 100644
--- a/src/core/surface/secure_channel_create.c
+++ b/src/core/surface/secure_channel_create.c
@@ -196,13 +196,13 @@
 
   if (grpc_find_security_connector_in_args(args) != NULL) {
     gpr_log(GPR_ERROR, "Cannot set security context in channel args.");
-    return grpc_lame_client_channel_create();
+    return grpc_lame_client_channel_create(target);
   }
 
   if (grpc_credentials_create_security_connector(
           creds, target, args, NULL, &connector, &new_args_from_connector) !=
       GRPC_SECURITY_OK) {
-    return grpc_lame_client_channel_create();
+    return grpc_lame_client_channel_create(target);
   }
   mdctx = grpc_mdctx_create();
 
@@ -231,7 +231,8 @@
     return NULL;
   }
 
-  channel = grpc_channel_create_from_filters(filters, n, args_copy, mdctx, 1);
+  channel =
+      grpc_channel_create_from_filters(target, filters, n, args_copy, mdctx, 1);
   grpc_client_channel_set_resolver(grpc_channel_get_channel_stack(channel),
                                    resolver);
   GRPC_RESOLVER_UNREF(resolver, "create");
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index 439452a..24f3529 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -722,6 +722,7 @@
     sizeof(channel_data),
     init_channel_elem,
     destroy_channel_elem,
+    grpc_call_next_get_peer,
     "server",
 };
 
@@ -878,8 +879,8 @@
     grpc_transport_perform_op(transport, &op);
   }
 
-  channel =
-      grpc_channel_create_from_filters(filters, num_filters, args, mdctx, 0);
+  channel = grpc_channel_create_from_filters(NULL, filters, num_filters, args,
+                                             mdctx, 0);
   chand = (channel_data *)grpc_channel_stack_element(
               grpc_channel_get_channel_stack(channel), 0)
               ->channel_data;
diff --git a/src/core/transport/chttp2/internal.h b/src/core/transport/chttp2/internal.h
index e5e6f44..e7901da 100644
--- a/src/core/transport/chttp2/internal.h
+++ b/src/core/transport/chttp2/internal.h
@@ -286,6 +286,7 @@
   grpc_endpoint *ep;
   grpc_mdctx *metadata_context;
   gpr_refcount refs;
+  char *peer_string;
 
   gpr_mu mu;
 
diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index c923d5e..eb435a2 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -168,6 +168,7 @@
 
   grpc_mdctx_unref(t->metadata_context);
 
+  gpr_free(t->peer_string);
   gpr_free(t);
 }
 
@@ -217,6 +218,7 @@
   gpr_ref_init(&t->refs, 2);
   gpr_mu_init(&t->mu);
   grpc_mdctx_ref(mdctx);
+  t->peer_string = grpc_endpoint_get_peer(ep);
   t->metadata_context = mdctx;
   t->endpoint_reading = 1;
   t->global.next_stream_id = is_client ? 1 : 2;
@@ -1069,9 +1071,17 @@
  * INTEGRATION GLUE
  */
 
-static const grpc_transport_vtable vtable = {
-    sizeof(grpc_chttp2_stream), init_stream,    perform_stream_op,
-    perform_transport_op,       destroy_stream, destroy_transport};
+static char *chttp2_get_peer(grpc_transport *t) {
+  return gpr_strdup(((grpc_chttp2_transport *)t)->peer_string);
+}
+
+static const grpc_transport_vtable vtable = {sizeof(grpc_chttp2_stream),
+                                             init_stream,
+                                             perform_stream_op,
+                                             perform_transport_op,
+                                             destroy_stream,
+                                             destroy_transport,
+                                             chttp2_get_peer};
 
 grpc_transport *grpc_create_chttp2_transport(
     const grpc_channel_args *channel_args, grpc_endpoint *ep, grpc_mdctx *mdctx,
diff --git a/src/core/transport/transport.c b/src/core/transport/transport.c
index 2689e30..69c00b6 100644
--- a/src/core/transport/transport.c
+++ b/src/core/transport/transport.c
@@ -65,6 +65,10 @@
   transport->vtable->destroy_stream(transport, stream);
 }
 
+char *grpc_transport_get_peer(grpc_transport *transport) {
+  return transport->vtable->get_peer(transport);
+}
+
 void grpc_transport_stream_op_finish_with_failure(
     grpc_transport_stream_op *op) {
   if (op->send_ops) {
diff --git a/src/core/transport/transport.h b/src/core/transport/transport.h
index 6450360..5537796 100644
--- a/src/core/transport/transport.h
+++ b/src/core/transport/transport.h
@@ -182,4 +182,7 @@
 /* Destroy the transport */
 void grpc_transport_destroy(grpc_transport *transport);
 
+/* Get the transports peer */
+char *grpc_transport_get_peer(grpc_transport *transport);
+
 #endif /* GRPC_INTERNAL_CORE_TRANSPORT_TRANSPORT_H */
diff --git a/src/core/transport/transport_impl.h b/src/core/transport/transport_impl.h
index 515721d..d3bbdf6 100644
--- a/src/core/transport/transport_impl.h
+++ b/src/core/transport/transport_impl.h
@@ -58,6 +58,9 @@
 
   /* implementation of grpc_transport_destroy */
   void (*destroy)(grpc_transport *self);
+
+  /* implementation of grpc_transport_get_peer */
+  char *(*get_peer)(grpc_transport *self);
 } grpc_transport_vtable;
 
 /* an instance of a grpc transport */
diff --git a/src/cpp/client/create_channel.cc b/src/cpp/client/create_channel.cc
index 38eeda0..62f179d 100644
--- a/src/cpp/client/create_channel.cc
+++ b/src/cpp/client/create_channel.cc
@@ -52,6 +52,6 @@
                     user_agent_prefix.str());
   return creds ? creds->CreateChannel(target, cp_args)
                : std::shared_ptr<ChannelInterface>(
-                     new Channel(target, grpc_lame_client_channel_create()));
+                     new Channel(target, grpc_lame_client_channel_create(NULL)));
 }
 }  // namespace grpc
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
index eca2a40..60129af 100644
--- a/test/core/channel/channel_stack_test.c
+++ b/test/core/channel/channel_stack_test.c
@@ -37,6 +37,8 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
 #include "test/core/util/test_config.h"
 
 static void channel_init_func(grpc_channel_element *elem, grpc_channel *master,
@@ -73,11 +75,14 @@
   ++*(int *)(elem->channel_data);
 }
 
+static char *get_peer(grpc_call_element *elem) { return gpr_strdup("peer"); }
+
 static void test_create_channel_stack(void) {
-  const grpc_channel_filter filter = {
-      call_func,         channel_func,         sizeof(int),
-      call_init_func,    call_destroy_func,    sizeof(int),
-      channel_init_func, channel_destroy_func, "some_test_filter"};
+  const grpc_channel_filter filter = {call_func,         channel_func,
+                                      sizeof(int),       call_init_func,
+                                      call_destroy_func, sizeof(int),
+                                      channel_init_func, channel_destroy_func,
+                                      get_peer,          "some_test_filter"};
   const grpc_channel_filter *filters = &filter;
   grpc_channel_stack *channel_stack;
   grpc_call_stack *call_stack;
diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c
index 05ad42c..453376c 100644
--- a/test/core/end2end/dualstack_socket_test.c
+++ b/test/core/end2end/dualstack_socket_test.c
@@ -37,6 +37,7 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/host_port.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 #include "test/core/end2end/cq_verifier.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
@@ -78,6 +79,7 @@
   size_t details_capacity = 0;
   int was_cancelled = 2;
   grpc_call_details call_details;
+  char *peer;
 
   if (port == 0) {
     port = grpc_pick_unused_port_or_die();
@@ -105,7 +107,12 @@
   cqv = cq_verifier_create(cq);
 
   /* Create client. */
-  gpr_join_host_port(&client_hostport, client_host, port);
+  if (client_host[0] == 'i') {
+    /* for ipv4:/ipv6: addresses, just concatenate the port */
+    gpr_asprintf(&client_hostport, "%s:%d", client_host, port);
+  } else {
+    gpr_join_host_port(&client_hostport, client_host, port);
+  }
   client = grpc_channel_create(client_hostport, NULL);
 
   gpr_log(GPR_INFO, "Testing with server=%s client=%s (expecting %s)",
@@ -179,6 +186,10 @@
     cq_expect_completion(cqv, tag(1), 1);
     cq_verify(cqv);
 
+    peer = grpc_call_get_peer(c);
+    gpr_log(GPR_DEBUG, "got peer: '%s'", peer);
+    gpr_free(peer);
+
     GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
     GPR_ASSERT(0 == strcmp(details, "xyz"));
     GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
@@ -237,21 +248,31 @@
     /* :: and 0.0.0.0 are handled identically. */
     test_connect("::", "127.0.0.1", 0, 1);
     test_connect("::", "::ffff:127.0.0.1", 0, 1);
+    test_connect("::", "ipv4:127.0.0.1", 0, 1);
+    test_connect("::", "ipv6:[::ffff:127.0.0.1]", 0, 1);
     test_connect("::", "localhost", 0, 1);
     test_connect("0.0.0.0", "127.0.0.1", 0, 1);
     test_connect("0.0.0.0", "::ffff:127.0.0.1", 0, 1);
+    test_connect("0.0.0.0", "ipv4:127.0.0.1", 0, 1);
+    test_connect("0.0.0.0", "ipv6:[::ffff:127.0.0.1]", 0, 1);
     test_connect("0.0.0.0", "localhost", 0, 1);
     if (do_ipv6) {
       test_connect("::", "::1", 0, 1);
       test_connect("0.0.0.0", "::1", 0, 1);
+      test_connect("::", "ipv6:[::1]", 0, 1);
+      test_connect("0.0.0.0", "ipv6:[::1]", 0, 1);
     }
 
     /* These only work when the families agree. */
     test_connect("127.0.0.1", "127.0.0.1", 0, 1);
+    test_connect("127.0.0.1", "ipv4:127.0.0.1", 0, 1);
     if (do_ipv6) {
       test_connect("::1", "::1", 0, 1);
       test_connect("::1", "127.0.0.1", 0, 0);
       test_connect("127.0.0.1", "::1", 0, 0);
+      test_connect("::1", "ipv6:[::1]", 0, 1);
+      test_connect("::1", "ipv4:127.0.0.1", 0, 0);
+      test_connect("127.0.0.1", "ipv6:[::1]", 0, 0);
     }
   }
 
diff --git a/test/core/end2end/fixtures/chttp2_socket_pair.c b/test/core/end2end/fixtures/chttp2_socket_pair.c
index 37b61cf..807fc8e 100644
--- a/test/core/end2end/fixtures/chttp2_socket_pair.c
+++ b/test/core/end2end/fixtures/chttp2_socket_pair.c
@@ -80,7 +80,7 @@
                                           &grpc_connected_channel_filter};
   size_t nfilters = sizeof(filters) / sizeof(*filters);
   grpc_channel *channel = grpc_channel_create_from_filters(
-      filters, nfilters, cs->client_args, mdctx, 1);
+      "socketpair-target", filters, nfilters, cs->client_args, mdctx, 1);
 
   cs->f->client = channel;
 
diff --git a/test/core/end2end/fixtures/chttp2_socket_pair_one_byte_at_a_time.c b/test/core/end2end/fixtures/chttp2_socket_pair_one_byte_at_a_time.c
index 2ec2697..21d4404 100644
--- a/test/core/end2end/fixtures/chttp2_socket_pair_one_byte_at_a_time.c
+++ b/test/core/end2end/fixtures/chttp2_socket_pair_one_byte_at_a_time.c
@@ -80,7 +80,7 @@
                                           &grpc_connected_channel_filter};
   size_t nfilters = sizeof(filters) / sizeof(*filters);
   grpc_channel *channel = grpc_channel_create_from_filters(
-      filters, nfilters, cs->client_args, mdctx, 1);
+      "socketpair-target", filters, nfilters, cs->client_args, mdctx, 1);
 
   cs->f->client = channel;
 
diff --git a/test/core/end2end/fixtures/chttp2_socket_pair_with_grpc_trace.c b/test/core/end2end/fixtures/chttp2_socket_pair_with_grpc_trace.c
index 3aa364c..c59628b 100644
--- a/test/core/end2end/fixtures/chttp2_socket_pair_with_grpc_trace.c
+++ b/test/core/end2end/fixtures/chttp2_socket_pair_with_grpc_trace.c
@@ -81,7 +81,7 @@
                                           &grpc_connected_channel_filter};
   size_t nfilters = sizeof(filters) / sizeof(*filters);
   grpc_channel *channel = grpc_channel_create_from_filters(
-      filters, nfilters, cs->client_args, mdctx, 1);
+      "socketpair-target", filters, nfilters, cs->client_args, mdctx, 1);
 
   cs->f->client = channel;
 
diff --git a/test/core/end2end/tests/simple_request.c b/test/core/end2end/tests/simple_request.c
index 6194b84..ca783af 100644
--- a/test/core/end2end/tests/simple_request.c
+++ b/test/core/end2end/tests/simple_request.c
@@ -114,11 +114,17 @@
   char *details = NULL;
   size_t details_capacity = 0;
   int was_cancelled = 2;
+  char *peer;
 
   c = grpc_channel_create_call(f.client, f.cq, "/foo",
                                "foo.test.google.fr:1234", deadline);
   GPR_ASSERT(c);
 
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != NULL);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
   grpc_metadata_array_init(&initial_metadata_recv);
   grpc_metadata_array_init(&trailing_metadata_recv);
   grpc_metadata_array_init(&request_metadata_recv);
@@ -151,6 +157,15 @@
   cq_expect_completion(cqv, tag(101), 1);
   cq_verify(cqv);
 
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != NULL);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != NULL);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
   op = ops;
   op->op = GRPC_OP_SEND_INITIAL_METADATA;
   op->data.send_initial_metadata.count = 0;
diff --git a/test/core/iomgr/tcp_posix_test.c b/test/core/iomgr/tcp_posix_test.c
index a23c649..4e7fb44 100644
--- a/test/core/iomgr/tcp_posix_test.c
+++ b/test/core/iomgr/tcp_posix_test.c
@@ -172,7 +172,7 @@
 
   create_sockets(sv);
 
-  ep = grpc_tcp_create(grpc_fd_create(sv[1], "read_test"), slice_size);
+  ep = grpc_tcp_create(grpc_fd_create(sv[1], "read_test"), slice_size, "test");
   grpc_endpoint_add_to_pollset(ep, &g_pollset);
 
   written_bytes = fill_socket_partial(sv[0], num_bytes);
@@ -207,7 +207,8 @@
 
   create_sockets(sv);
 
-  ep = grpc_tcp_create(grpc_fd_create(sv[1], "large_read_test"), slice_size);
+  ep = grpc_tcp_create(grpc_fd_create(sv[1], "large_read_test"), slice_size,
+                       "test");
   grpc_endpoint_add_to_pollset(ep, &g_pollset);
 
   written_bytes = fill_socket(sv[0]);
@@ -340,7 +341,7 @@
   create_sockets(sv);
 
   ep = grpc_tcp_create(grpc_fd_create(sv[1], "write_test"),
-                       GRPC_TCP_DEFAULT_READ_SLICE_SIZE);
+                       GRPC_TCP_DEFAULT_READ_SLICE_SIZE, "test");
   grpc_endpoint_add_to_pollset(ep, &g_pollset);
 
   state.ep = ep;
@@ -394,7 +395,7 @@
   create_sockets(sv);
 
   ep = grpc_tcp_create(grpc_fd_create(sv[1], "write_error_test"),
-                       GRPC_TCP_DEFAULT_READ_SLICE_SIZE);
+                       GRPC_TCP_DEFAULT_READ_SLICE_SIZE, "test");
   grpc_endpoint_add_to_pollset(ep, &g_pollset);
 
   close(sv[0]);
@@ -459,10 +460,10 @@
   grpc_endpoint_test_fixture f;
 
   create_sockets(sv);
-  f.client_ep =
-      grpc_tcp_create(grpc_fd_create(sv[0], "fixture:client"), slice_size);
-  f.server_ep =
-      grpc_tcp_create(grpc_fd_create(sv[1], "fixture:server"), slice_size);
+  f.client_ep = grpc_tcp_create(grpc_fd_create(sv[0], "fixture:client"),
+                                slice_size, "test");
+  f.server_ep = grpc_tcp_create(grpc_fd_create(sv[1], "fixture:server"),
+                                slice_size, "test");
   grpc_endpoint_add_to_pollset(f.client_ep, &g_pollset);
   grpc_endpoint_add_to_pollset(f.server_ep, &g_pollset);
 
diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c
index b2facd3..3a7a3a3 100644
--- a/test/core/surface/lame_client_test.c
+++ b/test/core/surface/lame_client_test.c
@@ -57,7 +57,7 @@
 
   grpc_metadata_array_init(&trailing_metadata_recv);
 
-  chan = grpc_lame_client_channel_create();
+  chan = grpc_lame_client_channel_create("lampoon:national");
   GPR_ASSERT(chan);
   cq = grpc_completion_queue_create();
   call = grpc_channel_create_call(chan, cq, "/Foo", "anywhere",
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 31ad56a..3e578c1 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -803,7 +803,7 @@
 src/core/client_config/resolver_factory.h \
 src/core/client_config/resolver_registry.h \
 src/core/client_config/resolvers/dns_resolver.h \
-src/core/client_config/resolvers/unix_resolver_posix.h \
+src/core/client_config/resolvers/sockaddr_resolver.h \
 src/core/client_config/subchannel.h \
 src/core/client_config/subchannel_factory.h \
 src/core/client_config/uri_parser.h \
@@ -881,6 +881,7 @@
 src/core/transport/transport.h \
 src/core/transport/transport_impl.h \
 src/core/census/context.h \
+src/core/census/rpc_stat_id.h \
 src/core/httpcli/format_request.c \
 src/core/httpcli/httpcli.c \
 src/core/httpcli/httpcli_security_connector.c \
@@ -922,7 +923,7 @@
 src/core/client_config/resolver_factory.c \
 src/core/client_config/resolver_registry.c \
 src/core/client_config/resolvers/dns_resolver.c \
-src/core/client_config/resolvers/unix_resolver_posix.c \
+src/core/client_config/resolvers/sockaddr_resolver.c \
 src/core/client_config/subchannel.c \
 src/core/client_config/subchannel_factory.c \
 src/core/client_config/uri_parser.c \
@@ -1016,6 +1017,7 @@
 src/core/transport/transport_op_string.c \
 src/core/census/context.c \
 src/core/census/initialize.c \
+src/core/census/record_stat.c \
 include/grpc/support/alloc.h \
 include/grpc/support/atm.h \
 include/grpc/support/atm_gcc_atomic.h \
diff --git a/tools/jenkins/run_distribution.sh b/tools/jenkins/run_distribution.sh
index 7c306de5..49b7d30 100755
--- a/tools/jenkins/run_distribution.sh
+++ b/tools/jenkins/run_distribution.sh
@@ -32,6 +32,8 @@
 # linuxbrew installation of a selected language
 set -ex
 
+# Our homebrew installation script command, per language
+# Can be used in both linux and macos
 if [ "$language" == "core" ]; then
   command="curl -fsSL https://goo.gl/getgrpc | bash -"
 elif [[ "python nodejs ruby php" =~ "$language" ]]; then
@@ -66,6 +68,7 @@
 elif [ "$platform" == "macos" ]; then
 
   if [ "$dist_channel" == "homebrew" ]; then
+
     echo "Formulas installed by system-wide homebrew (before)"
     brew list -l
 
@@ -99,8 +102,6 @@
       *php*)
         export CFLAGS="-Wno-parentheses-equality"
         ;;
-      *)
-        ;;
     esac
 
     # Run our homebrew installation script
@@ -108,7 +109,6 @@
 
     # Uninstall / clean up per-language modules/extensions after the test
     case $language in
-      *core*) ;;
       *python*)
         deactivate
         rm -rf jenkins_python_venv
@@ -124,10 +124,6 @@
       *php*)
         rm grpc.so
         ;;
-      *)
-        echo "Unsupported language $language"
-        exit 1
-        ;;
     esac
 
     # Clean up
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index abddaab..d6db928 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -9849,6 +9849,7 @@
       "include/grpc/status.h", 
       "src/core/census/context.h", 
       "src/core/census/grpc_context.h", 
+      "src/core/census/rpc_stat_id.h", 
       "src/core/channel/census_filter.h", 
       "src/core/channel/channel_args.h", 
       "src/core/channel/channel_stack.h", 
@@ -9867,7 +9868,7 @@
       "src/core/client_config/resolver_factory.h", 
       "src/core/client_config/resolver_registry.h", 
       "src/core/client_config/resolvers/dns_resolver.h", 
-      "src/core/client_config/resolvers/unix_resolver_posix.h", 
+      "src/core/client_config/resolvers/sockaddr_resolver.h", 
       "src/core/client_config/subchannel.h", 
       "src/core/client_config/subchannel_factory.h", 
       "src/core/client_config/uri_parser.h", 
@@ -9977,6 +9978,8 @@
       "src/core/census/grpc_context.c", 
       "src/core/census/grpc_context.h", 
       "src/core/census/initialize.c", 
+      "src/core/census/record_stat.c", 
+      "src/core/census/rpc_stat_id.h", 
       "src/core/channel/census_filter.h", 
       "src/core/channel/channel_args.c", 
       "src/core/channel/channel_args.h", 
@@ -10011,8 +10014,8 @@
       "src/core/client_config/resolver_registry.h", 
       "src/core/client_config/resolvers/dns_resolver.c", 
       "src/core/client_config/resolvers/dns_resolver.h", 
-      "src/core/client_config/resolvers/unix_resolver_posix.c", 
-      "src/core/client_config/resolvers/unix_resolver_posix.h", 
+      "src/core/client_config/resolvers/sockaddr_resolver.c", 
+      "src/core/client_config/resolvers/sockaddr_resolver.h", 
       "src/core/client_config/subchannel.c", 
       "src/core/client_config/subchannel.h", 
       "src/core/client_config/subchannel_factory.c", 
@@ -10311,6 +10314,7 @@
       "include/grpc/status.h", 
       "src/core/census/context.h", 
       "src/core/census/grpc_context.h", 
+      "src/core/census/rpc_stat_id.h", 
       "src/core/channel/census_filter.h", 
       "src/core/channel/channel_args.h", 
       "src/core/channel/channel_stack.h", 
@@ -10329,7 +10333,7 @@
       "src/core/client_config/resolver_factory.h", 
       "src/core/client_config/resolver_registry.h", 
       "src/core/client_config/resolvers/dns_resolver.h", 
-      "src/core/client_config/resolvers/unix_resolver_posix.h", 
+      "src/core/client_config/resolvers/sockaddr_resolver.h", 
       "src/core/client_config/subchannel.h", 
       "src/core/client_config/subchannel_factory.h", 
       "src/core/client_config/uri_parser.h", 
@@ -10421,6 +10425,8 @@
       "src/core/census/grpc_context.c", 
       "src/core/census/grpc_context.h", 
       "src/core/census/initialize.c", 
+      "src/core/census/record_stat.c", 
+      "src/core/census/rpc_stat_id.h", 
       "src/core/channel/census_filter.h", 
       "src/core/channel/channel_args.c", 
       "src/core/channel/channel_args.h", 
@@ -10455,8 +10461,8 @@
       "src/core/client_config/resolver_registry.h", 
       "src/core/client_config/resolvers/dns_resolver.c", 
       "src/core/client_config/resolvers/dns_resolver.h", 
-      "src/core/client_config/resolvers/unix_resolver_posix.c", 
-      "src/core/client_config/resolvers/unix_resolver_posix.h", 
+      "src/core/client_config/resolvers/sockaddr_resolver.c", 
+      "src/core/client_config/resolvers/sockaddr_resolver.h", 
       "src/core/client_config/subchannel.c", 
       "src/core/client_config/subchannel.h", 
       "src/core/client_config/subchannel_factory.c", 
diff --git a/vsprojects/grpc/grpc.vcxproj b/vsprojects/grpc/grpc.vcxproj
index 6f929a7..744627e 100644
--- a/vsprojects/grpc/grpc.vcxproj
+++ b/vsprojects/grpc/grpc.vcxproj
@@ -192,7 +192,7 @@
     <ClInclude Include="..\..\src\core\client_config\resolver_factory.h" />
     <ClInclude Include="..\..\src\core\client_config\resolver_registry.h" />
     <ClInclude Include="..\..\src\core\client_config\resolvers\dns_resolver.h" />
-    <ClInclude Include="..\..\src\core\client_config\resolvers\unix_resolver_posix.h" />
+    <ClInclude Include="..\..\src\core\client_config\resolvers\sockaddr_resolver.h" />
     <ClInclude Include="..\..\src\core\client_config\subchannel.h" />
     <ClInclude Include="..\..\src\core\client_config\subchannel_factory.h" />
     <ClInclude Include="..\..\src\core\client_config\uri_parser.h" />
@@ -270,6 +270,7 @@
     <ClInclude Include="..\..\src\core\transport\transport.h" />
     <ClInclude Include="..\..\src\core\transport\transport_impl.h" />
     <ClInclude Include="..\..\src\core\census\context.h" />
+    <ClInclude Include="..\..\src\core\census\rpc_stat_id.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\src\core\httpcli\format_request.c">
@@ -354,7 +355,7 @@
     </ClCompile>
     <ClCompile Include="..\..\src\core\client_config\resolvers\dns_resolver.c">
     </ClCompile>
-    <ClCompile Include="..\..\src\core\client_config\resolvers\unix_resolver_posix.c">
+    <ClCompile Include="..\..\src\core\client_config\resolvers\sockaddr_resolver.c">
     </ClCompile>
     <ClCompile Include="..\..\src\core\client_config\subchannel.c">
     </ClCompile>
@@ -542,6 +543,8 @@
     </ClCompile>
     <ClCompile Include="..\..\src\core\census\initialize.c">
     </ClCompile>
+    <ClCompile Include="..\..\src\core\census\record_stat.c">
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\gpr\gpr.vcxproj">
diff --git a/vsprojects/grpc/grpc.vcxproj.filters b/vsprojects/grpc/grpc.vcxproj.filters
index 0c38822..84a7823 100644
--- a/vsprojects/grpc/grpc.vcxproj.filters
+++ b/vsprojects/grpc/grpc.vcxproj.filters
@@ -124,7 +124,7 @@
     <ClCompile Include="..\..\src\core\client_config\resolvers\dns_resolver.c">
       <Filter>src\core\client_config\resolvers</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\src\core\client_config\resolvers\unix_resolver_posix.c">
+    <ClCompile Include="..\..\src\core\client_config\resolvers\sockaddr_resolver.c">
       <Filter>src\core\client_config\resolvers</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\core\client_config\subchannel.c">
@@ -406,6 +406,9 @@
     <ClCompile Include="..\..\src\core\census\initialize.c">
       <Filter>src\core\census</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\core\census\record_stat.c">
+      <Filter>src\core\census</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\include\grpc\grpc_security.h">
@@ -539,7 +542,7 @@
     <ClInclude Include="..\..\src\core\client_config\resolvers\dns_resolver.h">
       <Filter>src\core\client_config\resolvers</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\src\core\client_config\resolvers\unix_resolver_posix.h">
+    <ClInclude Include="..\..\src\core\client_config\resolvers\sockaddr_resolver.h">
       <Filter>src\core\client_config\resolvers</Filter>
     </ClInclude>
     <ClInclude Include="..\..\src\core\client_config\subchannel.h">
@@ -773,6 +776,9 @@
     <ClInclude Include="..\..\src\core\census\context.h">
       <Filter>src\core\census</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\core\census\rpc_stat_id.h">
+      <Filter>src\core\census</Filter>
+    </ClInclude>
   </ItemGroup>
 
   <ItemGroup>
diff --git a/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj
index 753f342..a573023 100644
--- a/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj
@@ -173,7 +173,7 @@
     <ClInclude Include="..\..\src\core\client_config\resolver_factory.h" />
     <ClInclude Include="..\..\src\core\client_config\resolver_registry.h" />
     <ClInclude Include="..\..\src\core\client_config\resolvers\dns_resolver.h" />
-    <ClInclude Include="..\..\src\core\client_config\resolvers\unix_resolver_posix.h" />
+    <ClInclude Include="..\..\src\core\client_config\resolvers\sockaddr_resolver.h" />
     <ClInclude Include="..\..\src\core\client_config\subchannel.h" />
     <ClInclude Include="..\..\src\core\client_config\subchannel_factory.h" />
     <ClInclude Include="..\..\src\core\client_config\uri_parser.h" />
@@ -251,6 +251,7 @@
     <ClInclude Include="..\..\src\core\transport\transport.h" />
     <ClInclude Include="..\..\src\core\transport\transport_impl.h" />
     <ClInclude Include="..\..\src\core\census\context.h" />
+    <ClInclude Include="..\..\src\core\census\rpc_stat_id.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\src\core\surface\init_unsecure.c">
@@ -289,7 +290,7 @@
     </ClCompile>
     <ClCompile Include="..\..\src\core\client_config\resolvers\dns_resolver.c">
     </ClCompile>
-    <ClCompile Include="..\..\src\core\client_config\resolvers\unix_resolver_posix.c">
+    <ClCompile Include="..\..\src\core\client_config\resolvers\sockaddr_resolver.c">
     </ClCompile>
     <ClCompile Include="..\..\src\core\client_config\subchannel.c">
     </ClCompile>
@@ -477,6 +478,8 @@
     </ClCompile>
     <ClCompile Include="..\..\src\core\census\initialize.c">
     </ClCompile>
+    <ClCompile Include="..\..\src\core\census\record_stat.c">
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\gpr\gpr.vcxproj">
diff --git a/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj.filters
index 1b312cc..c779043 100644
--- a/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -55,7 +55,7 @@
     <ClCompile Include="..\..\src\core\client_config\resolvers\dns_resolver.c">
       <Filter>src\core\client_config\resolvers</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\src\core\client_config\resolvers\unix_resolver_posix.c">
+    <ClCompile Include="..\..\src\core\client_config\resolvers\sockaddr_resolver.c">
       <Filter>src\core\client_config\resolvers</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\core\client_config\subchannel.c">
@@ -337,6 +337,9 @@
     <ClCompile Include="..\..\src\core\census\initialize.c">
       <Filter>src\core\census</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\core\census\record_stat.c">
+      <Filter>src\core\census</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\include\grpc\byte_buffer.h">
@@ -416,7 +419,7 @@
     <ClInclude Include="..\..\src\core\client_config\resolvers\dns_resolver.h">
       <Filter>src\core\client_config\resolvers</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\src\core\client_config\resolvers\unix_resolver_posix.h">
+    <ClInclude Include="..\..\src\core\client_config\resolvers\sockaddr_resolver.h">
       <Filter>src\core\client_config\resolvers</Filter>
     </ClInclude>
     <ClInclude Include="..\..\src\core\client_config\subchannel.h">
@@ -650,6 +653,9 @@
     <ClInclude Include="..\..\src\core\census\context.h">
       <Filter>src\core\census</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\core\census\rpc_stat_id.h">
+      <Filter>src\core\census</Filter>
+    </ClInclude>
   </ItemGroup>
 
   <ItemGroup>