Commit 7a7c4007 authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #533 from ohwgiles/master

RPC: Support abstract unix socket addresses on Linux
parents 8fffb02d d01db3c9
......@@ -28,6 +28,8 @@
#include "windows-sanity.h"
#else
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#endif
namespace kj {
......@@ -102,6 +104,7 @@ TEST(AsyncIo, AddressParsing) {
#if !_WIN32
EXPECT_EQ("unix:foo/bar/baz", tryParse(w, network, "unix:foo/bar/baz"));
EXPECT_EQ("unix-abstract:foo/bar/baz", tryParse(w, network, "unix-abstract:foo/bar/baz"));
#endif
// We can parse services by name...
......@@ -387,5 +390,27 @@ TEST(AsyncIo, Udp) {
#endif // !_WIN32
#ifdef __linux__ // Abstract unix sockets are only supported on Linux
TEST(AsyncIo, AbstractUnixSocket) {
auto ioContext = setupAsyncIo();
auto& network = ioContext.provider->getNetwork();
Own<NetworkAddress> addr = network.parseAddress("unix-abstract:foo").wait(ioContext.waitScope);
Own<ConnectionReceiver> listener = addr->listen();
// chdir proves no filesystem dependence. Test fails for regular unix socket
// but passes for abstract unix socket.
int originalDirFd;
KJ_SYSCALL(originalDirFd = open(".", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
KJ_DEFER(close(originalDirFd));
KJ_SYSCALL(chdir("/tmp"));
KJ_DEFER(KJ_SYSCALL(fchdir(originalDirFd)));
addr->connect().attach(kj::mv(listener)).wait(ioContext.waitScope);
}
#endif // __linux__
} // namespace
} // namespace kj
......@@ -449,7 +449,11 @@ public:
return str('[', buffer, "]:", ntohs(addr.inet6.sin6_port));
}
case AF_UNIX: {
return str("unix:", addr.unixDomain.sun_path);
if (addr.unixDomain.sun_path[0] == '\0') {
return str("unix-abstract:", addr.unixDomain.sun_path + 1);
} else {
return str("unix:", addr.unixDomain.sun_path);
}
}
default:
return str("(unknown address family ", addr.generic.sa_family, ")");
......@@ -470,6 +474,9 @@ public:
StringPtr path = str.slice(strlen("unix:"));
KJ_REQUIRE(path.size() < sizeof(addr.unixDomain.sun_path),
"Unix domain socket address is too long.", str);
KJ_REQUIRE(path.size() == strlen(path.cStr()),
"Unix domain socket address contains NULL. Use"
" 'unix-abstract:' for the abstract namespace.");
result.addr.unixDomain.sun_family = AF_UNIX;
strcpy(result.addr.unixDomain.sun_path, path.cStr());
result.addrlen = offsetof(struct sockaddr_un, sun_path) + path.size() + 1;
......@@ -478,6 +485,21 @@ public:
return array.finish();
}
if (str.startsWith("unix-abstract:")) {
StringPtr path = str.slice(strlen("unix-abstract:"));
KJ_REQUIRE(path.size() + 1 < sizeof(addr.unixDomain.sun_path),
"Unix domain socket address is too long.", str);
result.addr.unixDomain.sun_family = AF_UNIX;
result.addr.unixDomain.sun_path[0] = '\0';
// although not strictly required by Linux, also copy the trailing
// NULL terminator so that we can safely read it back in toString
memcpy(result.addr.unixDomain.sun_path + 1, path.cStr(), path.size() + 1);
result.addrlen = offsetof(struct sockaddr_un, sun_path) + path.size() + 1;
auto array = kj::heapArrayBuilder<SocketAddress>(1);
array.add(result);
return array.finish();
}
// Try to separate the address and port.
ArrayPtr<const char> addrPart;
Maybe<StringPtr> portPart;
......
......@@ -390,7 +390,8 @@ int main(int argc, const char* argv[]) {
{% endhighlight %}
Note that for the connect address, Cap'n Proto supports DNS host names as well as IPv4 and IPv6
addresses. Additionally, a Unix domain socket can be specified as `unix:` followed by a path name.
addresses. Additionally, a Unix domain socket can be specified as `unix:` followed by a path name,
and an abstract Unix domain socket can be specified as `unix-abstract:` followed by an identifier.
For a more complete example, see the
[calculator client sample](https://github.com/sandstorm-io/capnproto/tree/master/c++/samples/calculator-client.c++).
......@@ -429,7 +430,8 @@ int main(int argc, const char* argv[]) {
Note that for the bind address, Cap'n Proto supports DNS host names as well as IPv4 and IPv6
addresses. The special address `*` can be used to bind to the same port on all local IPv4 and
IPv6 interfaces. Additionally, a Unix domain socket can be specified as `unix:` followed by a
path name.
path name, and an abstract Unix domain socket can be specified as `unix-abstract:` followed by
an identifier.
For a more complete example, see the
[calculator server sample](https://github.com/sandstorm-io/capnproto/tree/master/c++/samples/calculator-server.c++).
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment