Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions changelog/std-socket-abstract.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
`std.socket.UnixAddress` now supports abstract addresses.

UNIX domain sockets are usually identified by pathnames. Linux offers a
non-portable extension to this scheme, known as abstract socket addresses,
which are independent of the filesystem. An abstract socket address starts with
a null byte (`'\0'`), e.g.:

---
auto addr = new UnixAddress("\0/tmp/dbus-OtHLWmCLPR");
---
113 changes: 85 additions & 28 deletions std/socket.d
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,20 @@ abstract class Address
/// Returns actual size of underlying $(D sockaddr) structure.
abstract @property socklen_t nameLen() const pure nothrow @nogc;

// Socket.remoteAddress, Socket.localAddress, and Socket.receiveFrom
// use setNameLen to set the actual size of the address as returned by
// getsockname, getpeername, and recvfrom, respectively.
// The following implementation is sufficient for fixed-length addresses,
// and ensures that the length is not changed.
// Must be overridden for variable-length addresses.
protected void setNameLen(socklen_t len)
{
if (len != this.nameLen)
throw new AddressException(
format("%s expects address of length %d, not %d", typeid(this),
this.nameLen, len), 0);
}

/// Family of this address.
@property AddressFamily addressFamily() const pure nothrow @nogc
{
Expand Down Expand Up @@ -1914,7 +1928,22 @@ version(StdDdoc)

/**
* $(D UnixAddress) encapsulates an address for a Unix domain socket
* ($(D AF_UNIX)). Available only on supported systems.
* ($(D AF_UNIX)), i.e. a socket bound to a path name in the file system.
* Available only on supported systems.
*
* Linux also supports an abstract address namespace, in which addresses
* are independent of the file system. A socket address is abstract
* iff `path` starts with a _null byte (`'\0'`). Null bytes in other
* positions of an abstract address are allowed and have no special
* meaning.
*
* Example:
* ---
* auto addr = new UnixAddress("/var/run/dbus/system_bus_socket");
* auto abstractAddr = new UnixAddress("\0/tmp/dbus-OtHLWmCLPR");
* ---
*
* See_Also: $(HTTP http://man7.org/linux/man-pages/man7/unix.7.html, UNIX(7))
*/
class UnixAddress: Address
{
Expand Down Expand Up @@ -1947,6 +1976,8 @@ static if (is(sockaddr_un))
class UnixAddress: Address
{
protected:
socklen_t _nameLen;

struct
{
align (1):
Expand All @@ -1958,6 +1989,14 @@ static if (is(sockaddr_un))
{
sun.sun_family = AddressFamily.UNIX;
sun.sun_path = '?';
_nameLen = sun.sizeof;
}

override void setNameLen(socklen_t len) @trusted
{
if (len > sun.sizeof)
throw new SocketParameterException("Not enough socket address storage");
_nameLen = len;
}

public:
Expand All @@ -1973,16 +2012,26 @@ static if (is(sockaddr_un))

override @property socklen_t nameLen() @trusted const
{
return cast(socklen_t) (sockaddr_un.init.sun_path.offsetof +
strlen(cast(const(char*)) sun.sun_path.ptr) + 1);
return _nameLen;
}

this(in char[] path) @trusted pure
{
enforce(path.length <= sun.sun_path.sizeof, new SocketParameterException("Path too long"));
sun.sun_family = AddressFamily.UNIX;
sun.sun_path.ptr[0 .. path.length] = (cast(byte[]) path)[];
sun.sun_path.ptr[path.length] = 0;
_nameLen = cast(socklen_t)
{
auto len = sockaddr_un.init.sun_path.offsetof + path.length;
// Pathname socket address must be terminated with '\0'
// which must be included in the address length.
if (sun.sun_path.ptr[0])
{
sun.sun_path.ptr[path.length] = 0;
++len;
}
return len;
}();
}

this(sockaddr_un addr) pure nothrow @nogc
Expand All @@ -1993,7 +2042,11 @@ static if (is(sockaddr_un))

@property string path() @trusted const pure
{
return to!string(cast(const(char)*)sun.sun_path.ptr);
auto len = _nameLen - sockaddr_un.init.sun_path.offsetof;
// For pathname socket address we need to strip off the terminating '\0'
if (sun.sun_path.ptr[0])
--len;
return (cast(const(char)*) sun.sun_path.ptr)[0 .. len].idup;
}

override string toString() const pure
Expand All @@ -2010,32 +2063,36 @@ static if (is(sockaddr_un))
immutable ubyte[] data = [1, 2, 3, 4];
Socket[2] pair;

auto name = deleteme ~ "-unix-socket";
auto address = new UnixAddress(name);
auto names = [ deleteme ~ "-unix-socket" ];
version (linux)
names ~= "\0" ~ deleteme ~ "-abstract\0unix\0socket";
foreach (name; names)
{
auto address = new UnixAddress(name);

auto listener = new Socket(AddressFamily.UNIX, SocketType.STREAM);
scope(exit) listener.close();
auto listener = new Socket(AddressFamily.UNIX, SocketType.STREAM);
scope(exit) listener.close();
listener.bind(address);
scope(exit) () @trusted { if (name[0]) remove(name.tempCString()); } ();
assert(listener.localAddress.toString == name);

listener.bind(address);
scope(exit) () @trusted { remove(name.tempCString()); } ();
assert(listener.localAddress.toString == name);
listener.listen(1);

listener.listen(1);
pair[0] = new Socket(AddressFamily.UNIX, SocketType.STREAM);
scope(exit) listener.close();

pair[0] = new Socket(AddressFamily.UNIX, SocketType.STREAM);
scope(exit) listener.close();
pair[0].connect(address);
scope(exit) pair[0].close();

pair[0].connect(address);
scope(exit) pair[0].close();
pair[1] = listener.accept();
scope(exit) pair[1].close();

pair[1] = listener.accept();
scope(exit) pair[1].close();
pair[0].send(data);

pair[0].send(data);

auto buf = new ubyte[data.length];
pair[1].receive(buf);
assert(buf == data);
auto buf = new ubyte[data.length];
pair[1].receive(buf);
assert(buf == data);
}
}
}

Expand Down Expand Up @@ -2889,8 +2946,7 @@ public:
socklen_t nameLen = addr.nameLen;
if (_SOCKET_ERROR == .getpeername(sock, addr.name, &nameLen))
throw new SocketOSException("Unable to obtain remote socket address");
if (nameLen > addr.nameLen)
throw new SocketParameterException("Not enough socket address storage");
addr.setNameLen(nameLen);
assert(addr.addressFamily == _family);
return addr;
}
Expand All @@ -2902,8 +2958,7 @@ public:
socklen_t nameLen = addr.nameLen;
if (_SOCKET_ERROR == .getsockname(sock, addr.name, &nameLen))
throw new SocketOSException("Unable to obtain local socket address");
if (nameLen > addr.nameLen)
throw new SocketParameterException("Not enough socket address storage");
addr.setNameLen(nameLen);
assert(addr.addressFamily == _family);
return addr;
}
Expand Down Expand Up @@ -3046,13 +3101,15 @@ public:
version(Windows)
{
auto read = .recvfrom(sock, buf.ptr, capToInt(buf.length), cast(int) flags, from.name, &nameLen);
from.setNameLen(nameLen);
assert(from.addressFamily == _family);
// if (!read) //connection closed
return read;
}
else
{
auto read = .recvfrom(sock, buf.ptr, buf.length, cast(int) flags, from.name, &nameLen);
from.setNameLen(nameLen);
assert(from.addressFamily == _family);
// if (!read) //connection closed
return read;
Expand Down