Add ipv4:, ipv6: schemes
diff --git a/BUILD b/BUILD
index e116d45..fefb1d9 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",
@@ -287,7 +287,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",
@@ -424,7 +424,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",
@@ -520,7 +520,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",
@@ -998,7 +998,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",
@@ -1137,7 +1137,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",
diff --git a/Makefile b/Makefile
index 4756b75..5117cbb 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 \
@@ -3781,7 +3781,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/build.json b/build.json
index 2755703..783fa80 100644
--- a/build.json
+++ b/build.json
@@ -129,7 +129,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 +225,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..56e6628 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',
@@ -296,7 +296,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',
@@ -434,7 +434,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',
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/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..d42f8b1
--- /dev/null
+++ b/src/core/client_config/resolvers/sockaddr_resolver.c
@@ -0,0 +1,297 @@
+/*
+ *
+ * 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>
+#include <sys/un.h>
+
+#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_aton(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/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/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/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c
index 7d3568c..7907589 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"
@@ -105,7 +106,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)",
@@ -233,21 +239,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/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 31ad56a..bcbb852 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 \
@@ -922,7 +922,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/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index abddaab..1d02f8b 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -9867,7 +9867,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", 
@@ -10011,8 +10011,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", 
@@ -10329,7 +10329,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", 
@@ -10455,8 +10455,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..c92e69d 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" />
@@ -354,7 +354,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>
diff --git a/vsprojects/grpc/grpc.vcxproj.filters b/vsprojects/grpc/grpc.vcxproj.filters
index 0c38822..b5dbf3d 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">
@@ -539,7 +539,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">
diff --git a/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj
index 753f342..0039ddd 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" />
@@ -289,7 +289,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>
diff --git a/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj.filters
index 1b312cc..cf04cb9 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">
@@ -416,7 +416,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">