From 03c99a6ba49ca4614dfa333bd0005f70ad9368df Mon Sep 17 00:00:00 2001 From: Luca Bruno Date: Thu, 26 Oct 2017 09:30:55 +0000 Subject: [PATCH] sys/socket: add UnixAddr abstract name getter This introduces an `abstract_name()` getter to `UnixAddr` in order to retrieve the name of an abstract unix socket. This also adds tests around abstract addresses and clarify docs, adding explicit semantics. --- src/sys/socket/addr.rs | 29 +++++++++++++++++++++++------ test/sys/test_socket.rs | 21 +++++++++++++++++++++ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index d57207ed50..a8aaf10783 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -521,7 +521,9 @@ impl fmt::Display for Ipv6Addr { * */ -/// A wrapper around `sockaddr_un`. We track the length of `sun_path` (excluding +/// A wrapper around `sockaddr_un`. +/// +/// This also tracks the length of `sun_path` address (excluding /// a terminating null), because it may not be null-terminated. For example, /// unconnected and Linux abstract sockets are never null-terminated, and POSIX /// does not require that `sun_len` include the terminating null even for normal @@ -555,10 +557,12 @@ impl UnixAddr { })) } - /// Create a new sockaddr_un representing an address in the - /// "abstract namespace". This is a Linux-specific extension, - /// primarily used to allow chrooted processes to communicate with - /// specific daemons. + /// Create a new `sockaddr_un` representing an address in the "abstract namespace". + /// + /// The leading null byte for the abstract namespace is automatically added; + /// thus the input `path` is expected to be the bare name, not null-prefixed. + /// This is a Linux-specific extension, primarily used to allow chrooted + /// processes to communicate with processes having a different filesystem view. pub fn new_abstract(path: &[u8]) -> Result { unsafe { let mut ret = libc::sockaddr_un { @@ -587,7 +591,7 @@ impl UnixAddr { /// If this address represents a filesystem path, return that path. pub fn path(&self) -> Option<&Path> { if self.1 == 0 || self.0.sun_path[0] == 0 { - // unbound or abstract + // unnamed or abstract None } else { let p = self.sun_path(); @@ -600,6 +604,19 @@ impl UnixAddr { Some(Path::new(::from_bytes(&p[..reallen]))) } } + + /// If this address represents an abstract socket, return its name. + /// + /// For abstract sockets only the bare name is returned, without the + /// leading null byte. `None` is returned for unnamed or path-backed sockets. + pub fn abstract_name(&self) -> Option<&[u8]> { + if self.1 >= 1 && self.0.sun_path[0] == 0 { + Some(&self.sun_path()[1..]) + } else { + // unnamed or filesystem path + None + } + } } impl PartialEq for UnixAddr { diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 5f9c6fff07..5ada6d901f 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -64,6 +64,27 @@ pub fn test_path_to_sock_addr() { assert_eq!(addr.path(), Some(actual)); } +// Test getting/setting abstract addresses (without unix socket creation) +#[test] +pub fn test_abstract_uds_addr() { + let empty = String::new(); + let addr = UnixAddr::new_abstract(empty.as_bytes()).unwrap(); + assert_eq!(addr.abstract_name(), Some(empty.as_bytes())); + + let name = String::from("nix\0abstract\0test"); + let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + assert_eq!(addr.abstract_name(), Some(name.as_bytes())); + assert_eq!(addr.path(), None); + + // Internally, name is null-prefixed (abstract namespace) + let internal: &[u8] = unsafe { + slice::from_raw_parts(addr.0.sun_path.as_ptr() as *const u8, addr.1) + }; + let mut abstract_name = name.clone(); + abstract_name.insert(0, '\0'); + assert_eq!(internal, abstract_name.as_bytes()); +} + #[test] pub fn test_getsockname() { use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag};