Skip to content

Add abstract namespace support for Unix domain sockets #85379

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 16, 2021
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
88 changes: 86 additions & 2 deletions library/std/src/os/unix/net/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ impl<'a> fmt::Display for AsciiEscaped<'a> {
#[derive(Clone)]
#[stable(feature = "unix_socket", since = "1.10.0")]
pub struct SocketAddr {
addr: libc::sockaddr_un,
len: libc::socklen_t,
pub(super) addr: libc::sockaddr_un,
pub(super) len: libc::socklen_t,
}

impl SocketAddr {
Expand Down Expand Up @@ -196,6 +196,31 @@ impl SocketAddr {
if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None }
}

/// Returns the contents of this address if it is an abstract namespace
/// without the leading null byte.
///
/// # Examples
///
/// ```no_run
/// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixListener, SocketAddr};
///
/// fn main() -> std::io::Result<()> {
/// let namespace = b"hidden";
/// let namespace_addr = SocketAddr::from_abstract_namespace(&namespace[..])?;
/// let socket = UnixListener::bind_addr(&namespace_addr)?;
/// let local_addr = socket.local_addr().expect("Couldn't get local address");
/// assert_eq!(local_addr.as_abstract_namespace(), Some(&namespace[..]));
/// Ok(())
/// }
/// ```
#[doc(cfg(any(target_os = "android", target_os = "linux")))]
#[cfg(any(doc, target_os = "android", target_os = "linux",))]
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None }
}

fn address(&self) -> AddressKind<'_> {
let len = self.len as usize - sun_path_offset(&self.addr);
let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) };
Expand All @@ -212,6 +237,65 @@ impl SocketAddr {
AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
}
}

/// Creates an abstract domain socket address from a namespace
///
/// An abstract address does not create a file unlike traditional path-based
/// Unix sockets. The advantage of this is that the address will disappear when
/// the socket bound to it is closed, so no filesystem clean up is required.
///
/// The leading null byte for the abstract namespace is automatically added.
///
/// This is a Linux-specific extension. See more at [`unix(7)`].
///
/// [`unix(7)`]: https://man7.org/linux/man-pages/man7/unix.7.html
///
/// # Errors
///
/// This will return an error if the given namespace is too long
///
/// # Examples
///
/// ```no_run
/// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixListener, SocketAddr};
///
/// fn main() -> std::io::Result<()> {
/// let addr = SocketAddr::from_abstract_namespace(b"hidden")?;
/// let listener = match UnixListener::bind_addr(&addr) {
/// Ok(sock) => sock,
/// Err(err) => {
/// println!("Couldn't bind: {:?}", err);
/// return Err(err);
/// }
/// };
/// Ok(())
/// }
/// ```
#[doc(cfg(any(target_os = "android", target_os = "linux")))]
#[cfg(any(doc, target_os = "android", target_os = "linux",))]
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
pub fn from_abstract_namespace(namespace: &[u8]) -> io::Result<SocketAddr> {
unsafe {
let mut addr: libc::sockaddr_un = mem::zeroed();
addr.sun_family = libc::AF_UNIX as libc::sa_family_t;

if namespace.len() + 1 > addr.sun_path.len() {
return Err(io::Error::new_const(
io::ErrorKind::InvalidInput,
&"namespace must be shorter than SUN_LEN",
));
}

crate::ptr::copy_nonoverlapping(
namespace.as_ptr(),
addr.sun_path.as_mut_ptr().offset(1) as *mut u8,
namespace.len(),
);
let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t;
SocketAddr::from_parts(addr, len)
}
}
}

#[stable(feature = "unix_socket", since = "1.10.0")]
Expand Down
108 changes: 107 additions & 1 deletion library/std/src/os/unix/net/datagram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,41 @@ impl UnixDatagram {
}
}

/// Creates a Unix datagram socket bound to an address.
///
/// # Examples
///
/// ```no_run
/// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixDatagram};
///
/// fn main() -> std::io::Result<()> {
/// let sock1 = UnixDatagram::bind("path/to/socket")?;
/// let addr = sock1.local_addr()?;
///
/// let sock2 = match UnixDatagram::bind_addr(&addr) {
/// Ok(sock) => sock,
/// Err(err) => {
/// println!("Couldn't bind: {:?}", err);
/// return Err(err);
/// }
/// };
/// Ok(())
/// }
/// ```
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixDatagram> {
unsafe {
let socket = UnixDatagram::unbound()?;
cvt(libc::bind(
socket.as_raw_fd(),
&socket_addr.addr as *const _ as *const _,
socket_addr.len as _,
))?;
Ok(socket)
}
}

/// Creates a Unix Datagram socket which is not bound to any address.
///
/// # Examples
Expand Down Expand Up @@ -156,7 +191,7 @@ impl UnixDatagram {
Ok((UnixDatagram(i1), UnixDatagram(i2)))
}

/// Connects the socket to the specified address.
/// Connects the socket to the specified path address.
///
/// The [`send`] method may be used to send data to the specified address.
/// [`recv`] and [`recv_from`] will only receive data from that address.
Expand Down Expand Up @@ -192,6 +227,41 @@ impl UnixDatagram {
Ok(())
}

/// Connects the socket to an address.
///
/// # Examples
///
/// ```no_run
/// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixDatagram};
///
/// fn main() -> std::io::Result<()> {
/// let bound = UnixDatagram::bind("/path/to/socket")?;
/// let addr = bound.local_addr()?;
///
/// let sock = UnixDatagram::unbound()?;
/// match sock.connect_addr(&addr) {
/// Ok(sock) => sock,
/// Err(e) => {
/// println!("Couldn't connect: {:?}", e);
/// return Err(e)
/// }
/// };
/// Ok(())
/// }
/// ```
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
pub fn connect_addr(&self, socket_addr: &SocketAddr) -> io::Result<()> {
unsafe {
cvt(libc::connect(
self.as_raw_fd(),
&socket_addr.addr as *const _ as *const _,
socket_addr.len,
))?;
}
Ok(())
}

/// Creates a new independently owned handle to the underlying socket.
///
/// The returned `UnixDatagram` is a reference to the same socket that this
Expand Down Expand Up @@ -473,6 +543,42 @@ impl UnixDatagram {
}
}

/// Sends data on the socket to the specified [SocketAddr].
///
/// On success, returns the number of bytes written.
///
/// [SocketAddr]: crate::os::unix::net::SocketAddr
///
/// # Examples
///
/// ```no_run
/// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixDatagram};
///
/// fn main() -> std::io::Result<()> {
/// let bound = UnixDatagram::bind("/path/to/socket")?;
/// let addr = bound.local_addr()?;
///
/// let sock = UnixDatagram::unbound()?;
/// sock.send_to_addr(b"bacon egg and cheese", &addr).expect("send_to_addr function failed");
/// Ok(())
/// }
/// ```
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
pub fn send_to_addr(&self, buf: &[u8], socket_addr: &SocketAddr) -> io::Result<usize> {
unsafe {
let count = cvt(libc::sendto(
self.as_raw_fd(),
buf.as_ptr() as *const _,
buf.len(),
MSG_NOSIGNAL,
&socket_addr.addr as *const _ as *const _,
socket_addr.len,
))?;
Ok(count as usize)
}
}

/// Sends data on the socket to the socket's peer.
///
/// The peer address may be set by the `connect` method, and this method
Expand Down
38 changes: 38 additions & 0 deletions library/std/src/os/unix/net/listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,44 @@ impl UnixListener {
}
}

/// Creates a new `UnixListener` bound to the specified [`socket address`].
///
/// [`socket address`]: crate::os::unix::net::SocketAddr
///
/// # Examples
///
/// ```no_run
/// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixListener};
///
/// fn main() -> std::io::Result<()> {
/// let listener1 = UnixListener::bind("path/to/socket")?;
/// let addr = listener1.local_addr()?;
///
/// let listener2 = match UnixListener::bind_addr(&addr) {
/// Ok(sock) => sock,
/// Err(err) => {
/// println!("Couldn't bind: {:?}", err);
/// return Err(err);
/// }
/// };
/// Ok(())
/// }
/// ```
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixListener> {
unsafe {
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
cvt(libc::bind(
inner.as_raw_fd(),
&socket_addr.addr as *const _ as *const _,
socket_addr.len as _,
))?;
cvt(libc::listen(inner.as_raw_fd(), 128))?;
Ok(UnixListener(inner))
}
}

/// Accepts a new incoming connection to this listener.
///
/// This function will block the calling thread until a new Unix connection
Expand Down
37 changes: 37 additions & 0 deletions library/std/src/os/unix/net/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,43 @@ impl UnixStream {
}
}

/// Connects to the socket specified by [`address`].
///
/// [`address`]: crate::os::unix::net::SocketAddr
///
/// # Examples
///
/// ```no_run
/// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixListener, UnixStream};
///
/// fn main() -> std::io::Result<()> {
/// let listener = UnixListener::bind("/path/to/the/socket")?;
/// let addr = listener.local_addr()?;
///
/// let sock = match UnixStream::connect_addr(&addr) {
/// Ok(sock) => sock,
/// Err(e) => {
/// println!("Couldn't connect: {:?}", e);
/// return Err(e)
/// }
/// };
/// Ok(())
/// }
/// ````
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result<UnixStream> {
unsafe {
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
cvt(libc::connect(
inner.as_raw_fd(),
&socket_addr.addr as *const _ as *const _,
socket_addr.len,
))?;
Ok(UnixStream(inner))
}
}

/// Creates an unnamed pair of connected sockets.
///
/// Returns two `UnixStream`s which are connected to each other.
Expand Down
Loading