Add support for "unix-abstract:" URIs to support abstract unix domain sockets

Adapted from #21278 by @mheese
pull/24500/head
Benjamin Shaya 4 years ago
parent 8288687032
commit 702edbeee4
  1. 13
      doc/naming.md
  2. 20
      src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc
  3. 16
      src/core/ext/transport/chttp2/server/chttp2_server.cc
  4. 90
      src/core/lib/iomgr/parse_address.cc
  5. 20
      src/core/lib/iomgr/parse_address.h
  6. 6
      src/core/lib/iomgr/resolve_address_posix.cc
  7. 42
      src/core/lib/iomgr/unix_sockets_posix.cc
  8. 5
      src/core/lib/iomgr/unix_sockets_posix.h
  9. 7
      src/core/lib/iomgr/unix_sockets_posix_noop.cc
  10. 10
      test/core/client_channel/resolvers/sockaddr_resolver_test.cc
  11. 29
      test/core/end2end/fixtures/h2_uds.cc
  12. 21
      test/core/iomgr/parse_address_test.cc
  13. 38
      test/core/iomgr/resolve_address_posix_test.cc

@ -34,13 +34,24 @@ Most gRPC implementations support the following URI schemes:
resolver does not support this, but the c-ares based resolver
supports specifying this in the form "IP:port".)
- `unix:path` or `unix://absolute_path` -- Unix domain sockets (Unix systems only)
- `unix:path`, `unix://absolute_path` -- Unix domain sockets (Unix systems only)
- `path` indicates the location of the desired socket.
- In the first form, the path may be relative or absolute; in the
second form, the path must be absolute (i.e., there will actually be
three slashes, two prior to the path and another to begin the
absolute path).
- `unix-abstract:abstract_path` -- Unix domain socket in abstract namespace (Unix systems only)
- `abstract_path` indicates a name in the abstract namespace.
- The name has no connection with filesystem pathnames.
- No permissions will apply to the socket - any process/user may access the socket.
- The underlying implementation of Abstract sockets uses a null byte ('\0')
as the first character; the implementation will prepend this null. Do not include
the null in `abstract_path`.
- `abstract_path` cannot contain null bytes.
- TODO(https://github.com/grpc/grpc/issues/24638): Unix allows abstract socket names to contain null bytes,
but this is not supported by the gRPC C-core implementation.
The following schemes are supported by the gRPC C-core implementation,
but may not be supported in other languages:

@ -168,6 +168,24 @@ class UnixResolverFactory : public ResolverFactory {
const char* scheme() const override { return "unix"; }
};
class UnixAbstractResolverFactory : public ResolverFactory {
public:
bool IsValidUri(const grpc_uri* uri) const override {
return ParseUri(uri, grpc_parse_unix_abstract, nullptr);
}
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
return CreateSockaddrResolver(std::move(args), grpc_parse_unix_abstract);
}
grpc_core::UniquePtr<char> GetDefaultAuthority(
grpc_uri* /*uri*/) const override {
return grpc_core::UniquePtr<char>(gpr_strdup("localhost"));
}
const char* scheme() const override { return "unix-abstract"; }
};
#endif // GRPC_HAVE_UNIX_SOCKET
} // namespace
@ -182,6 +200,8 @@ void grpc_resolver_sockaddr_init() {
#ifdef GRPC_HAVE_UNIX_SOCKET
grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
absl::make_unique<grpc_core::UnixResolverFactory>());
grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
absl::make_unique<grpc_core::UnixAbstractResolverFactory>());
#endif
}

@ -25,6 +25,7 @@
#include <string.h>
#include <vector>
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
@ -46,6 +47,7 @@
#include "src/core/lib/iomgr/resolve_address.h"
#include "src/core/lib/iomgr/resource_quota.h"
#include "src/core/lib/iomgr/tcp_server.h"
#include "src/core/lib/iomgr/unix_sockets_posix.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/server.h"
@ -53,6 +55,9 @@
namespace grpc_core {
namespace {
const char kUnixUriPrefix[] = "unix:";
const char kUnixAbstractUriPrefix[] = "unix-abstract:";
class Chttp2ServerListener : public Server::ListenerInterface {
public:
static grpc_error* Create(Server* server, const char* addr,
@ -278,7 +283,16 @@ grpc_error* Chttp2ServerListener::Create(Server* server, const char* addr,
grpc_error* error = [&]() {
*port_num = -1;
/* resolve address */
grpc_error* error = grpc_blocking_resolve_address(addr, "https", &resolved);
grpc_error* error = GRPC_ERROR_NONE;
if (absl::StartsWith(addr, kUnixUriPrefix)) {
error = grpc_resolve_unix_domain_address(
addr + sizeof(kUnixUriPrefix) - 1, &resolved);
} else if (absl::StartsWith(addr, kUnixAbstractUriPrefix)) {
error = grpc_resolve_unix_abstract_domain_address(
addr + sizeof(kUnixAbstractUriPrefix) - 1, &resolved);
} else {
error = grpc_blocking_resolve_address(addr, "https", &resolved);
}
if (error != GRPC_ERROR_NONE) return error;
// Create Chttp2ServerListener.
listener = new Chttp2ServerListener(server, args);

@ -18,6 +18,8 @@
#include <grpc/support/port_platform.h>
#include "absl/strings/str_cat.h"
#include "src/core/lib/iomgr/grpc_if_nametoindex.h"
#include "src/core/lib/iomgr/parse_address.h"
#include "src/core/lib/iomgr/sockaddr.h"
@ -49,24 +51,98 @@ bool grpc_parse_unix(const grpc_uri* uri,
gpr_log(GPR_ERROR, "Expected 'unix' scheme, got '%s'", uri->scheme);
return false;
}
grpc_error* error = grpc_core::UnixSockaddrPopulate(uri->path, resolved_addr);
if (error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
GRPC_ERROR_UNREF(error);
return false;
}
return true;
}
bool grpc_parse_unix_abstract(const grpc_uri* uri,
grpc_resolved_address* resolved_addr) {
if (strcmp("unix-abstract", uri->scheme) != 0) {
gpr_log(GPR_ERROR, "Expected 'unix-abstract' scheme, got '%s'",
uri->scheme);
return false;
}
grpc_error* error =
grpc_core::UnixAbstractSockaddrPopulate(uri->path, resolved_addr);
if (error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
GRPC_ERROR_UNREF(error);
return false;
}
return true;
}
namespace grpc_core {
grpc_error* UnixSockaddrPopulate(absl::string_view path,
grpc_resolved_address* resolved_addr) {
struct sockaddr_un* un =
reinterpret_cast<struct sockaddr_un*>(resolved_addr->addr);
const size_t maxlen = sizeof(un->sun_path);
const size_t path_len = strnlen(uri->path, maxlen);
if (path_len == maxlen) return false;
const size_t maxlen = sizeof(un->sun_path) - 1;
if (path.size() > maxlen) {
return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("Path name should not have more than ", maxlen,
" characters")
.c_str());
}
un->sun_family = AF_UNIX;
strcpy(un->sun_path, uri->path);
path.copy(un->sun_path, path.size());
un->sun_path[path.size()] = '\0';
resolved_addr->len = static_cast<socklen_t>(sizeof(*un));
return true;
return GRPC_ERROR_NONE;
}
#else /* GRPC_HAVE_UNIX_SOCKET */
grpc_error* UnixAbstractSockaddrPopulate(absl::string_view path,
grpc_resolved_address* resolved_addr) {
struct sockaddr_un* un =
reinterpret_cast<struct sockaddr_un*>(resolved_addr->addr);
const size_t maxlen = sizeof(un->sun_path) - 1;
if (path.size() > maxlen) {
return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("Path name should not have more than ", maxlen,
" characters")
.c_str());
}
un->sun_family = AF_UNIX;
un->sun_path[0] = '\0';
path.copy(un->sun_path + 1, path.size());
resolved_addr->len =
static_cast<socklen_t>(sizeof(un->sun_family) + path.size() + 1);
return GRPC_ERROR_NONE;
}
} // namespace grpc_core
#else /* GRPC_HAVE_UNIX_SOCKET */
bool grpc_parse_unix(const grpc_uri* uri,
grpc_resolved_address* resolved_addr) {
abort();
}
bool grpc_parse_unix_abstract(const grpc_uri* uri,
grpc_resolved_address* resolved_addr) {
abort();
}
namespace grpc_core {
grpc_error* UnixSockaddrPopulate(absl::string_view path,
grpc_resolved_address* resolved_addr) {
abort();
}
grpc_error* UnixAbstractSockaddrPopulate(absl::string_view path,
grpc_resolved_address* resolved_addr) {
abort();
}
} // namespace grpc_core
#endif /* GRPC_HAVE_UNIX_SOCKET */
bool grpc_parse_ipv4_hostport(const char* hostport, grpc_resolved_address* addr,
@ -219,6 +295,8 @@ bool grpc_parse_ipv6(const grpc_uri* uri,
bool grpc_parse_uri(const grpc_uri* uri, grpc_resolved_address* resolved_addr) {
if (strcmp("unix", uri->scheme) == 0) {
return grpc_parse_unix(uri, resolved_addr);
} else if (strcmp("unix-abstract", uri->scheme) == 0) {
return grpc_parse_unix_abstract(uri, resolved_addr);
} else if (strcmp("ipv4", uri->scheme) == 0) {
return grpc_parse_ipv4(uri, resolved_addr);
} else if (strcmp("ipv6", uri->scheme) == 0) {

@ -23,6 +23,8 @@
#include <stddef.h>
#include "absl/strings/string_view.h"
#include "src/core/lib/iomgr/resolve_address.h"
#include "src/core/lib/uri/uri_parser.h"
@ -30,6 +32,11 @@
* unix socket path. Returns true upon success. */
bool grpc_parse_unix(const grpc_uri* uri, grpc_resolved_address* resolved_addr);
/** Populate \a resolved_addr from \a uri, whose path is expected to contain a
* unix socket path in the abstract namespace. Returns true upon success. */
bool grpc_parse_unix_abstract(const grpc_uri* uri,
grpc_resolved_address* resolved_addr);
/** Populate \a resolved_addr from \a uri, whose path is expected to contain an
* IPv4 host:port pair. Returns true upon success. */
bool grpc_parse_ipv4(const grpc_uri* uri, grpc_resolved_address* resolved_addr);
@ -50,4 +57,17 @@ bool grpc_parse_ipv6_hostport(const char* hostport, grpc_resolved_address* addr,
/* Converts named or numeric port to a uint16 suitable for use in a sockaddr. */
uint16_t grpc_strhtons(const char* port);
namespace grpc_core {
/** Populate \a resolved_addr to be a unix socket at |path| */
grpc_error* UnixSockaddrPopulate(absl::string_view path,
grpc_resolved_address* resolved_addr);
/** Populate \a resolved_addr to be a unix socket in the abstract namespace
* at |path| */
grpc_error* UnixAbstractSockaddrPopulate(absl::string_view path,
grpc_resolved_address* resolved_addr);
} // namespace grpc_core
#endif /* GRPC_CORE_LIB_IOMGR_PARSE_ADDRESS_H */

@ -52,11 +52,6 @@ static grpc_error* posix_blocking_resolve_address(
size_t i;
grpc_error* err;
if (name[0] == 'u' && name[1] == 'n' && name[2] == 'i' && name[3] == 'x' &&
name[4] == ':' && name[5] != 0) {
return grpc_resolve_unix_domain_address(name + 5, addresses);
}
std::string host;
std::string port;
/* parse name, splitting it into host and port parts */
@ -67,6 +62,7 @@ static grpc_error* posix_blocking_resolve_address(
GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name));
goto done;
}
if (port.empty()) {
if (default_port == nullptr) {
err = grpc_error_set_str(

@ -30,6 +30,7 @@
#include "absl/strings/str_cat.h"
#include "src/core/lib/iomgr/parse_address.h"
#include "src/core/lib/iomgr/unix_sockets_posix.h"
#include <grpc/support/alloc.h>
@ -43,25 +44,22 @@ void grpc_create_socketpair_if_unix(int sv[2]) {
grpc_error* grpc_resolve_unix_domain_address(const char* name,
grpc_resolved_addresses** addrs) {
struct sockaddr_un* un;
if (strlen(name) >
GPR_ARRAY_SIZE(((struct sockaddr_un*)nullptr)->sun_path) - 1) {
return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("Path name should not have more than ",
GPR_ARRAY_SIZE(un->sun_path) - 1, " characters")
.c_str());
}
*addrs = static_cast<grpc_resolved_addresses*>(
gpr_malloc(sizeof(grpc_resolved_addresses)));
(*addrs)->naddrs = 1;
(*addrs)->addrs = static_cast<grpc_resolved_address*>(
gpr_malloc(sizeof(grpc_resolved_address)));
un = reinterpret_cast<struct sockaddr_un*>((*addrs)->addrs->addr);
un->sun_family = AF_UNIX;
strncpy(un->sun_path, name, sizeof(un->sun_path));
(*addrs)->addrs->len =
static_cast<socklen_t>(strlen(un->sun_path) + sizeof(un->sun_family) + 1);
return GRPC_ERROR_NONE;
return grpc_core::UnixSockaddrPopulate(name, (*addrs)->addrs);
}
grpc_error* grpc_resolve_unix_abstract_domain_address(
const absl::string_view name, grpc_resolved_addresses** addrs) {
*addrs = static_cast<grpc_resolved_addresses*>(
gpr_malloc(sizeof(grpc_resolved_addresses)));
(*addrs)->naddrs = 1;
(*addrs)->addrs = static_cast<grpc_resolved_address*>(
gpr_malloc(sizeof(grpc_resolved_address)));
return grpc_core::UnixAbstractSockaddrPopulate(name, (*addrs)->addrs);
}
int grpc_is_unix_socket(const grpc_resolved_address* resolved_addr) {
@ -79,8 +77,13 @@ void grpc_unlink_if_unix_domain_socket(
}
struct sockaddr_un* un = reinterpret_cast<struct sockaddr_un*>(
const_cast<char*>(resolved_addr->addr));
struct stat st;
// There is nothing to unlink for an abstract unix socket
if (un->sun_path[0] == '\0' && un->sun_path[1] != '\0') {
return;
}
struct stat st;
if (stat(un->sun_path, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) {
unlink(un->sun_path);
}
@ -93,6 +96,15 @@ std::string grpc_sockaddr_to_uri_unix_if_possible(
if (addr->sa_family != AF_UNIX) {
return "";
}
if (((struct sockaddr_un*)addr)->sun_path[0] == '\0' &&
((struct sockaddr_un*)addr)->sun_path[1] != '\0') {
const struct sockaddr_un* un =
reinterpret_cast<const struct sockaddr_un*>(resolved_addr->addr);
return absl::StrCat(
"unix-abstract:",
absl::string_view(un->sun_path + 1,
resolved_addr->len - sizeof(un->sun_family) - 1));
}
return absl::StrCat("unix:", ((struct sockaddr_un*)addr)->sun_path);
}

@ -29,11 +29,16 @@
#include "src/core/lib/iomgr/resolve_address.h"
#include "absl/strings/string_view.h"
void grpc_create_socketpair_if_unix(int sv[2]);
grpc_error* grpc_resolve_unix_domain_address(
const char* name, grpc_resolved_addresses** addresses);
grpc_error* grpc_resolve_unix_abstract_domain_address(
absl::string_view name, grpc_resolved_addresses** addresses);
int grpc_is_unix_socket(const grpc_resolved_address* resolved_addr);
void grpc_unlink_if_unix_domain_socket(

@ -40,6 +40,13 @@ grpc_error* grpc_resolve_unix_domain_address(
"Unix domain sockets are not supported on Windows");
}
grpc_error* grpc_resolve_unix_abstract_domain_address(
absl::string_view, grpc_resolved_addresses** addresses) {
*addresses = NULL;
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Unix domain sockets are not supported on Windows");
}
int grpc_is_unix_socket(const grpc_resolved_address* addr) { return false; }
void grpc_unlink_if_unix_domain_socket(const grpc_resolved_address* addr) {}

@ -101,6 +101,16 @@ int main(int argc, char** argv) {
test_fails(ipv6, "ipv6:[::]:123456");
test_fails(ipv6, "ipv6:www.google.com");
#ifdef GRPC_HAVE_UNIX_SOCKET
grpc_core::ResolverFactory* uds =
grpc_core::ResolverRegistry::LookupResolverFactory("unix");
grpc_core::ResolverFactory* uds_abstract =
grpc_core::ResolverRegistry::LookupResolverFactory("unix-abstract");
test_succeeds(uds, "unix:///tmp/sockaddr_resolver_test");
test_succeeds(uds_abstract, "unix-abstract:sockaddr_resolver_test");
#endif // GRPC_HAVE_UNIX_SOCKET
grpc_shutdown();
return 0;

@ -46,11 +46,10 @@ struct fullstack_fixture_data {
static int unique = 1;
static grpc_end2end_test_fixture chttp2_create_fixture_fullstack(
grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/) {
static grpc_end2end_test_fixture chttp2_create_fixture_fullstack_base(
std::string addr) {
fullstack_fixture_data* ffd = new fullstack_fixture_data;
ffd->localaddr = absl::StrFormat("unix:/tmp/grpc_fullstack_test.%d.%d",
getpid(), unique++);
ffd->localaddr = std::move(addr);
grpc_end2end_test_fixture f;
memset(&f, 0, sizeof(f));
@ -61,6 +60,21 @@ static grpc_end2end_test_fixture chttp2_create_fixture_fullstack(
return f;
}
static grpc_end2end_test_fixture chttp2_create_fixture_fullstack(
grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/) {
const std::string localaddr = absl::StrFormat(
"unix:/tmp/grpc_fullstack_test.%d.%d", getpid(), unique++);
return chttp2_create_fixture_fullstack_base(localaddr);
}
static grpc_end2end_test_fixture
chttp2_create_fixture_fullstack_abstract_namespace(
grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/) {
const std::string localaddr = absl::StrFormat(
"unix-abstract:grpc_fullstack_test.%d.%d", getpid(), unique++);
return chttp2_create_fixture_fullstack_base(localaddr);
}
void chttp2_init_client_fullstack(grpc_end2end_test_fixture* f,
grpc_channel_args* client_args) {
fullstack_fixture_data* ffd =
@ -97,6 +111,13 @@ static grpc_end2end_test_config configs[] = {
FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER,
nullptr, chttp2_create_fixture_fullstack, chttp2_init_client_fullstack,
chttp2_init_server_fullstack, chttp2_tear_down_fullstack},
{"chttp2/fullstack_uds_abstract_namespace",
FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL |
FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER,
nullptr, chttp2_create_fixture_fullstack_abstract_namespace,
chttp2_init_client_fullstack, chttp2_init_server_fullstack,
chttp2_tear_down_fullstack},
};
int main(int argc, char** argv) {

@ -39,7 +39,7 @@ static void test_grpc_parse_unix(const char* uri_text, const char* pathname) {
grpc_uri* uri = grpc_uri_parse(uri_text, false);
grpc_resolved_address addr;
GPR_ASSERT(1 == grpc_parse_unix(uri, &addr));
GPR_ASSERT(1 == grpc_parse_uri(uri, &addr));
struct sockaddr_un* addr_un =
reinterpret_cast<struct sockaddr_un*>(addr.addr);
GPR_ASSERT(AF_UNIX == addr_un->sun_family);
@ -48,9 +48,27 @@ static void test_grpc_parse_unix(const char* uri_text, const char* pathname) {
grpc_uri_destroy(uri);
}
static void test_grpc_parse_unix_abstract(const char* uri_text,
const char* pathname) {
grpc_core::ExecCtx exec_ctx;
grpc_uri* uri = grpc_uri_parse(uri_text, false);
grpc_resolved_address addr;
GPR_ASSERT(1 == grpc_parse_uri(uri, &addr));
struct sockaddr_un* addr_un =
reinterpret_cast<struct sockaddr_un*>(addr.addr);
GPR_ASSERT(AF_UNIX == addr_un->sun_family);
GPR_ASSERT('\0' == addr_un->sun_path[0]);
GPR_ASSERT(0 == strncmp(addr_un->sun_path + 1, pathname, strlen(pathname)));
grpc_uri_destroy(uri);
}
#else /* GRPC_HAVE_UNIX_SOCKET */
static void test_grpc_parse_unix(const char* uri_text, const char* pathname) {}
static void test_grpc_parse_unix_abstract(const char* uri_text,
const char* pathname) {}
#endif /* GRPC_HAVE_UNIX_SOCKET */
@ -105,6 +123,7 @@ int main(int argc, char** argv) {
grpc_init();
test_grpc_parse_unix("unix:/path/name", "/path/name");
test_grpc_parse_unix_abstract("unix-abstract:foobar", "foobar");
test_grpc_parse_ipv4("ipv4:192.0.2.1:12345", "192.0.2.1", 12345);
test_grpc_parse_ipv6("ipv6:[2001:db8::1]:12345", "2001:db8::1", 12345, 0);
test_grpc_parse_ipv6("ipv6:[2001:db8::1%252]:12345", "2001:db8::1", 12345, 2);

@ -138,40 +138,6 @@ static void must_fail(void* argsp, grpc_error* err) {
gpr_mu_unlock(args->mu);
}
static void test_unix_socket(void) {
grpc_core::ExecCtx exec_ctx;
args_struct args;
args_init(&args);
poll_pollset_until_request_done(&args);
grpc_resolve_address(
"unix:/path/name", nullptr, args.pollset_set,
GRPC_CLOSURE_CREATE(must_succeed, &args, grpc_schedule_on_exec_ctx),
&args.addrs);
args_finish(&args);
}
static void test_unix_socket_path_name_too_long(void) {
grpc_core::ExecCtx exec_ctx;
args_struct args;
args_init(&args);
const char prefix[] = "unix:/path/name";
size_t path_name_length =
GPR_ARRAY_SIZE(((struct sockaddr_un*)nullptr)->sun_path) + 6;
char* path_name =
static_cast<char*>(gpr_malloc(sizeof(char) * path_name_length));
memset(path_name, 'a', path_name_length);
memcpy(path_name, prefix, strlen(prefix) - 1);
path_name[path_name_length - 1] = '\0';
poll_pollset_until_request_done(&args);
grpc_resolve_address(
path_name, nullptr, args.pollset_set,
GRPC_CLOSURE_CREATE(must_fail, &args, grpc_schedule_on_exec_ctx),
&args.addrs);
gpr_free(path_name);
args_finish(&args);
}
static void resolve_address_must_succeed(const char* target) {
grpc_core::ExecCtx exec_ctx;
args_struct args;
@ -250,10 +216,6 @@ int main(int argc, char** argv) {
// unconditionally use the native DNS resolver).
grpc_core::UniquePtr<char> resolver =
GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
if (gpr_stricmp(resolver.get(), "native") == 0) {
test_unix_socket();
test_unix_socket_path_name_too_long();
}
}
gpr_cmdline_destroy(cl);

Loading…
Cancel
Save