#![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
}
}
#[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);
}
}
}