diff options
-rw-r--r-- | src/sys/socket/addr.rs | 29 | ||||
-rw-r--r-- | test/sys/test_socket.rs | 71 |
2 files changed, 99 insertions, 1 deletions
diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index c7b5f29e..f051d205 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -885,6 +885,29 @@ impl UnixAddr { } } + /// Create a new `sockaddr_un` representing an "unnamed" unix socket address. + pub fn new_unnamed() -> UnixAddr { + #[allow(unused)] + let mut ret = libc::sockaddr_un { + sun_family: AddressFamily::Unix as sa_family_t, + .. unsafe { mem::zeroed() } + }; + + let sun_len: u8 = offset_of!(libc::sockaddr_un, sun_path).try_into().unwrap(); + + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + { + ret.sun_len = sun_len; + } + + unsafe { UnixAddr::from_raw_parts(ret, sun_len) } + } + /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `sun_len` /// is the size of the valid portion of the struct, excluding any trailing /// NUL. @@ -941,6 +964,12 @@ impl UnixAddr { } } + /// Check if this address is an "unnamed" unix socket address. + #[inline] + pub fn is_unnamed(&self) -> bool { + matches!(self.kind(), UnixAddrKind::Unnamed) + } + /// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)` #[inline] pub fn path_len(&self) -> usize { diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 7ab60ecc..26ba9b2d 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -244,6 +244,22 @@ pub fn test_abstract_uds_addr() { assert_eq!(unsafe { (*addr.as_ptr()).sun_path[0] }, 0); } +// Test getting an unnamed address (without unix socket creation) +#[test] +pub fn test_unnamed_uds_addr() { + use crate::nix::sys::socket::SockaddrLike; + + let addr = UnixAddr::new_unnamed(); + + assert!(addr.is_unnamed()); + assert_eq!(addr.len(), 2); + assert!(addr.path().is_none()); + assert_eq!(addr.path_len(), 0); + + #[cfg(target_os = "linux")] + assert!(addr.as_abstract().is_none()); +} + #[test] pub fn test_getsockname() { use nix::sys::socket::bind; @@ -1484,7 +1500,7 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec<u8>) { // Test creating and using named unix domain sockets #[test] -pub fn test_unixdomain() { +pub fn test_named_unixdomain() { use nix::sys::socket::{accept, bind, connect, listen, socket, UnixAddr}; use nix::sys::socket::{SockFlag, SockType}; use nix::unistd::{close, read, write}; @@ -1527,6 +1543,59 @@ pub fn test_unixdomain() { assert_eq!(&buf[..], b"hello"); } +// Test using unnamed unix domain addresses +#[test] +pub fn test_unnamed_unixdomain() { + use nix::sys::socket::{getsockname, socketpair}; + use nix::sys::socket::{SockFlag, SockType}; + use nix::unistd::close; + + let (fd_1, fd_2) = socketpair( + AddressFamily::Unix, + SockType::Stream, + None, + SockFlag::empty(), + ) + .expect("socketpair failed"); + + let addr_1: UnixAddr = getsockname(fd_1).expect("getsockname failed"); + assert!(addr_1.is_unnamed()); + assert_eq!(addr_1, UnixAddr::new_unnamed()); + + close(fd_1).unwrap(); + close(fd_2).unwrap(); +} + +// Test creating and using unnamed unix domain addresses for autobinding sockets +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +pub fn test_unnamed_unixdomain_autobind() { + use nix::sys::socket::{bind, getsockname, socket}; + use nix::sys::socket::{SockFlag, SockType}; + use nix::unistd::close; + + let fd = socket( + AddressFamily::Unix, + SockType::Stream, + SockFlag::empty(), + None, + ) + .expect("socket failed"); + + // unix(7): "If a bind(2) call specifies addrlen as `sizeof(sa_family_t)`, or [...], then the + // socket is autobound to an abstract address" + bind(fd, &UnixAddr::new_unnamed()).expect("bind failed"); + + let addr: UnixAddr = getsockname(fd).expect("getsockname failed"); + let addr = addr.as_abstract().unwrap(); + + // changed from 8 to 5 bytes in Linux 2.3.15, and rust's minimum supported Linux version is 3.2 + // (as of 2022-11) + assert_eq!(addr.len(), 5); + + close(fd).unwrap(); +} + // Test creating and using named system control sockets #[cfg(any(target_os = "macos", target_os = "ios"))] #[test] |