use std::io;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::net as unix;
use std::path::Path;
#[derive(Clone, Copy)]
pub(crate) struct UnixAddr {
pub(crate) raw: libc::sockaddr_un,
pub(crate) len: libc::socklen_t,
}
impl UnixAddr {
pub(crate) fn path(path: &Path) -> io::Result<UnixAddr> {
let bytes = path.as_os_str().as_bytes();
if has_nul(bytes) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"the socket path holds a NUL byte",
));
}
Self::from_bytes(bytes, 1)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) fn abstract_name(name: &[u8]) -> io::Result<UnixAddr> {
let mut with_nul = [0u8; 108];
let len = name.len() + 1;
if len > with_nul.len() {
return Err(too_long());
}
with_nul[1..len].copy_from_slice(name);
Self::from_bytes(&with_nul[..len], 0)
}
pub(crate) fn of(addr: &unix::SocketAddr) -> io::Result<UnixAddr> {
if let Some(path) = addr.as_pathname() {
return Self::path(path);
}
#[cfg(any(target_os = "linux", target_os = "android"))]
{
#[cfg(target_os = "android")]
use std::os::android::net::SocketAddrExt;
#[cfg(target_os = "linux")]
use std::os::linux::net::SocketAddrExt;
if let Some(name) = addr.as_abstract_name() {
return Self::abstract_name(name);
}
}
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"an unnamed Unix socket address names no socket",
))
}
fn from_bytes(bytes: &[u8], extra: usize) -> io::Result<UnixAddr> {
let mut raw: libc::sockaddr_un = unsafe { core::mem::zeroed() };
if bytes.len() + extra > raw.sun_path.len() {
return Err(too_long());
}
raw.sun_family = libc::AF_UNIX as libc::sa_family_t;
let path = unsafe {
core::slice::from_raw_parts_mut(raw.sun_path.as_mut_ptr().cast::<u8>(), bytes.len())
};
path.copy_from_slice(bytes);
let len = core::mem::offset_of!(libc::sockaddr_un, sun_path) + bytes.len() + extra;
Ok(UnixAddr {
raw,
len: len as libc::socklen_t,
})
}
pub(crate) fn as_ptr(&self) -> *const libc::sockaddr {
(&self.raw as *const libc::sockaddr_un).cast()
}
}
fn has_nul(bytes: &[u8]) -> bool {
!unsafe { libc::memchr(bytes.as_ptr().cast(), 0, bytes.len()) }.is_null()
}
fn too_long() -> io::Error {
io::Error::new(
io::ErrorKind::InvalidInput,
"the socket address is too long",
)
}
impl core::fmt::Debug for UnixAddr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match unix_addr(&self.raw, self.len) {
Ok(addr) => write!(f, "{addr:?}"),
Err(_) => f.write_str("(unnamed)"),
}
}
}
pub(crate) fn unix_addr(
storage: &libc::sockaddr_un,
len: libc::socklen_t,
) -> io::Result<unix::SocketAddr> {
let base = core::mem::offset_of!(libc::sockaddr_un, sun_path);
let path_len = (len as usize)
.saturating_sub(base)
.min(storage.sun_path.len());
let raw: &[u8] =
unsafe { core::slice::from_raw_parts(storage.sun_path.as_ptr().cast(), path_len) };
#[cfg(any(target_os = "linux", target_os = "android"))]
if let [0, name @ ..] = raw
&& !name.is_empty()
{
#[cfg(target_os = "android")]
use std::os::android::net::SocketAddrExt;
#[cfg(target_os = "linux")]
use std::os::linux::net::SocketAddrExt;
return unix::SocketAddr::from_abstract_name(name);
}
let path = raw.split(|&b| b == 0).next().unwrap_or_default();
if path.is_empty() {
return unnamed();
}
unix::SocketAddr::from_pathname(std::ffi::OsStr::from_bytes(path))
}
fn unnamed() -> io::Result<unix::SocketAddr> {
unix::SocketAddr::from_pathname("")
}
#[cfg(test)]
mod tests;