#![deny(unsafe_code)]
use std::os::unix::ffi::OsStrExt;
use memchr::memchr;
use nix::sys::socket::{SockaddrLike, UnixAddr};
const SUN_PATH_OFFSET: usize = std::mem::offset_of!(libc::sockaddr_un, sun_path);
pub fn unix_path_bytes(addr: &UnixAddr) -> Option<&[u8]> {
addr.path().map(|path| {
let path = path.as_os_str().as_bytes();
&path[..memchr(0, path).unwrap_or(path.len())]
})
}
#[expect(clippy::cast_possible_truncation)]
pub(crate) fn unix_addr_len(addr: &UnixAddr) -> libc::socklen_t {
if let Some(path) = unix_path_bytes(addr) {
SUN_PATH_OFFSET.saturating_add(path.len()).saturating_add(1) as libc::socklen_t
} else if addr.as_abstract().is_some() {
addr.len()
} else {
SUN_PATH_OFFSET as libc::socklen_t
}
}
#[expect(clippy::cast_possible_truncation)]
pub(crate) fn unix_addr_len2(addr: &UnixAddr) -> libc::socklen_t {
unix_addr_len(addr).min(std::mem::size_of::<libc::sockaddr_un>() as libc::socklen_t)
}
#[expect(clippy::cast_possible_truncation)]
pub(crate) fn unix_addr_bytes(
addr: &UnixAddr,
) -> (
[u8; std::mem::size_of::<libc::sockaddr_un>() + 1],
libc::socklen_t,
) {
let mut buf = [0u8; std::mem::size_of::<libc::sockaddr_un>() + 1];
buf[..std::mem::size_of::<libc::sa_family_t>()]
.copy_from_slice(&(libc::AF_UNIX as libc::sa_family_t).to_ne_bytes());
if let Some(path) = unix_path_bytes(addr) {
buf[SUN_PATH_OFFSET..SUN_PATH_OFFSET.saturating_add(path.len())].copy_from_slice(path);
} else if let Some(name) = addr.as_abstract() {
let off = SUN_PATH_OFFSET.saturating_add(1);
buf[off..off.saturating_add(name.len())].copy_from_slice(name);
}
(buf, unix_addr_len(addr))
}
#[cfg(test)]
#[expect(unsafe_code)]
mod tests {
use std::mem::size_of;
use nix::sys::socket::{SockaddrLike, SockaddrStorage};
use super::*;
#[test]
fn test_unix_path_bytes_1() {
let addr = UnixAddr::new("/tmp/test.sock").unwrap();
assert_eq!(unix_path_bytes(&addr), Some(b"/tmp/test.sock".as_slice()));
}
#[test]
fn test_unix_path_bytes_2() {
let addr = UnixAddr::new("/a").unwrap();
assert_eq!(unix_path_bytes(&addr), Some(b"/a".as_slice()));
}
#[test]
fn test_unix_path_bytes_3() {
let long = "/".to_owned() + &"x".repeat(106);
assert_eq!(long.len(), 107);
let addr = UnixAddr::new(long.as_str()).unwrap();
assert_eq!(unix_path_bytes(&addr).unwrap(), long.as_bytes());
}
#[test]
fn test_unix_path_bytes_4() {
let addr = UnixAddr::new_unnamed();
assert_eq!(unix_path_bytes(&addr), None);
}
#[test]
fn test_unix_path_bytes_5() {
let addr = UnixAddr::new_abstract(b"foo").unwrap();
assert_eq!(unix_path_bytes(&addr), None);
}
#[test]
fn test_unix_path_bytes_6() {
let addr = UnixAddr::new("/tmp/test.sock").unwrap();
let full_len = size_of::<libc::sockaddr_un>() as libc::socklen_t;
let storage =
unsafe { SockaddrStorage::from_raw(addr.as_ptr().cast(), Some(full_len)) }.unwrap();
let recovered = storage.as_unix_addr().unwrap();
assert!(recovered.path().unwrap().as_os_str().as_bytes().len() > 14);
assert_eq!(
unix_path_bytes(recovered),
Some(b"/tmp/test.sock".as_slice())
);
}
#[test]
fn test_unix_path_bytes_7() {
let addr = UnixAddr::new("/x").unwrap();
let full_len = size_of::<libc::sockaddr_un>() as libc::socklen_t;
let storage =
unsafe { SockaddrStorage::from_raw(addr.as_ptr().cast(), Some(full_len)) }.unwrap();
let recovered = storage.as_unix_addr().unwrap();
assert_eq!(unix_path_bytes(recovered), Some(b"/x".as_slice()));
}
#[test]
fn test_unix_addr_len_1() {
let addr = UnixAddr::new("/tmp/test.sock").unwrap();
assert_eq!(unix_addr_len(&addr), 17);
}
#[test]
fn test_unix_addr_len_2() {
let addr = UnixAddr::new("/tmp/.syd_addrlen_srv.sock").unwrap();
assert_eq!(unix_addr_len(&addr), 29);
}
#[test]
fn test_unix_addr_len_3() {
let addr = UnixAddr::new_unnamed();
assert_eq!(unix_addr_len(&addr), SUN_PATH_OFFSET as libc::socklen_t);
assert_eq!(unix_addr_len(&addr), 2);
}
#[test]
fn test_unix_addr_len_4() {
let addr = UnixAddr::new_abstract(b"foo").unwrap();
assert_eq!(unix_addr_len(&addr), addr.len());
}
#[test]
fn test_unix_addr_len_5() {
let addr = UnixAddr::new_abstract(b"").unwrap();
assert_eq!(unix_addr_len(&addr), addr.len());
}
#[test]
fn test_unix_addr_len_6() {
let addr = UnixAddr::new("/tmp/.syd_addrlen_srv.sock").unwrap();
let full_len = size_of::<libc::sockaddr_un>() as libc::socklen_t;
let storage =
unsafe { SockaddrStorage::from_raw(addr.as_ptr().cast(), Some(full_len)) }.unwrap();
let recovered = storage.as_unix_addr().unwrap();
assert_eq!(recovered.len(), full_len);
assert_eq!(unix_addr_len(recovered), 29);
}
#[test]
fn test_unix_addr_len_7() {
let addr = UnixAddr::new("/a").unwrap();
let full_len = size_of::<libc::sockaddr_un>() as libc::socklen_t;
let storage =
unsafe { SockaddrStorage::from_raw(addr.as_ptr().cast(), Some(full_len)) }.unwrap();
let recovered = storage.as_unix_addr().unwrap();
assert_eq!(unix_addr_len(recovered), 5);
}
#[test]
fn test_unix_addr_len_8() {
let long = "/".to_owned() + &"x".repeat(106);
let addr = UnixAddr::new(long.as_str()).unwrap();
let full_len = size_of::<libc::sockaddr_un>() as libc::socklen_t;
let storage =
unsafe { SockaddrStorage::from_raw(addr.as_ptr().cast(), Some(full_len)) }.unwrap();
let recovered = storage.as_unix_addr().unwrap();
assert_eq!(unix_addr_len(recovered), 110);
}
#[test]
fn test_unix_addr_len_9() {
for path in [
"/a",
"/tmp/x",
"/tmp/test.sock",
"/tmp/.syd_addrlen_da.sock",
"/tmp/.syd_addrlen_srv.sock",
"/run/user/1000/bus",
"/var/run/nscd/socket",
] {
let expected = (SUN_PATH_OFFSET + path.len() + 1) as libc::socklen_t;
let addr = UnixAddr::new(path).unwrap();
assert_eq!(unix_addr_len(&addr), expected);
let full_len = size_of::<libc::sockaddr_un>() as libc::socklen_t;
let storage =
unsafe { SockaddrStorage::from_raw(addr.as_ptr().cast(), Some(full_len)) }.unwrap();
let recovered = storage.as_unix_addr().unwrap();
assert_eq!(unix_addr_len(recovered), expected);
}
}
#[test]
fn test_unix_addr_len_10() {
let mut raw: libc::sockaddr_un = unsafe { std::mem::zeroed() };
raw.sun_family = libc::sa_family_t::try_from(libc::AF_UNIX).unwrap();
for b in &mut raw.sun_path {
*b = b'x' as libc::c_char;
}
let full_len = size_of::<libc::sockaddr_un>() as libc::socklen_t;
let storage =
unsafe { SockaddrStorage::from_raw(std::ptr::addr_of!(raw).cast(), Some(full_len)) }
.unwrap();
let recovered = storage.as_unix_addr().unwrap();
assert_eq!(unix_path_bytes(recovered).unwrap().len(), 108);
assert_eq!(unix_addr_len(recovered), 111);
assert_eq!(unix_addr_len2(recovered), 110);
}
#[test]
fn test_unix_addr_bytes_1() {
let addr = UnixAddr::new("/tmp/test.sock").unwrap();
let (buf, len) = unix_addr_bytes(&addr);
assert_eq!(len, 17);
assert_eq!(&buf[2..16], b"/tmp/test.sock".as_slice());
assert_eq!(buf[16], 0);
}
#[test]
fn test_unix_addr_bytes_2() {
let addr = UnixAddr::new_abstract(b"foo").unwrap();
let (buf, len) = unix_addr_bytes(&addr);
assert_eq!(len, addr.len());
assert_eq!(buf[2], 0);
assert_eq!(&buf[3..6], b"foo".as_slice());
}
#[test]
fn test_unix_addr_bytes_3() {
let mut raw: libc::sockaddr_un = unsafe { std::mem::zeroed() };
raw.sun_family = libc::sa_family_t::try_from(libc::AF_UNIX).unwrap();
for b in &mut raw.sun_path {
*b = b'x' as libc::c_char;
}
let full_len = size_of::<libc::sockaddr_un>() as libc::socklen_t;
let storage =
unsafe { SockaddrStorage::from_raw(std::ptr::addr_of!(raw).cast(), Some(full_len)) }
.unwrap();
let recovered = storage.as_unix_addr().unwrap();
let (buf, len) = unix_addr_bytes(recovered);
assert_eq!(len, 111);
assert_eq!(buf[2..110], [b'x'; 108]);
assert_eq!(buf[110], 0);
}
}