use std::{
ascii,
cmp::Ordering,
ffi::OsStr,
fmt, io, mem,
os::unix::prelude::{FromRawFd, OsStrExt, RawFd},
path::Path,
};
use super::path_offset;
#[derive(Clone)]
pub struct SocketAddr {
sockaddr: libc::sockaddr_un,
socklen: libc::socklen_t,
}
struct AsciiEscaped<'a>(&'a [u8]);
enum AddressKind<'a> {
Unnamed,
Pathname(&'a Path),
Abstract(&'a [u8]),
}
impl SocketAddr {
fn address(&self) -> AddressKind<'_> {
let offset = path_offset(&self.sockaddr);
if (self.socklen as usize) < offset {
return AddressKind::Unnamed;
}
let len = self.socklen as usize - offset;
let path = unsafe { &*(&self.sockaddr.sun_path as *const [libc::c_char] as *const [u8]) };
if len == 0 {
AddressKind::Unnamed
} else if self.sockaddr.sun_path[0] == 0 {
AddressKind::Abstract(&path[1..len])
} else {
AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
}
}
#[allow(unused)]
pub(crate) fn new<F>(f: F) -> io::Result<SocketAddr>
where
F: FnOnce(*mut libc::sockaddr, &mut libc::socklen_t) -> io::Result<libc::c_int>,
{
let mut sockaddr = {
let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
unsafe { sockaddr.assume_init() }
};
let raw_sockaddr = &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr;
let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t;
f(raw_sockaddr, &mut socklen)?;
Ok(SocketAddr::from_parts(sockaddr, socklen))
}
pub(crate) fn from_parts(sockaddr: libc::sockaddr_un, socklen: libc::socklen_t) -> SocketAddr {
SocketAddr { sockaddr, socklen }
}
pub(crate) fn into_parts(self) -> (libc::sockaddr_un, libc::socklen_t) {
(self.sockaddr, self.socklen)
}
#[allow(clippy::match_like_matches_macro)]
pub fn is_unnamed(&self) -> bool {
if let AddressKind::Unnamed = self.address() {
true
} else {
false
}
}
pub fn as_pathname(&self) -> Option<&Path> {
if let AddressKind::Pathname(path) = self.address() {
Some(path)
} else {
None
}
}
pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
if let AddressKind::Abstract(path) = self.address() {
Some(path)
} else {
None
}
}
}
impl fmt::Debug for SocketAddr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.address() {
AddressKind::Unnamed => write!(fmt, "(unnamed)"),
AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)),
AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path),
}
}
}
impl<'a> fmt::Display for AsciiEscaped<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "\"")?;
for byte in self.0.iter().cloned().flat_map(ascii::escape_default) {
write!(fmt, "{}", byte as char)?;
}
write!(fmt, "\"")
}
}
pub(crate) fn socket_addr(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
let mut sockaddr = unsafe { sockaddr.assume_init() };
sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
let bytes = path.as_os_str().as_bytes();
match (bytes.first(), bytes.len().cmp(&sockaddr.sun_path.len())) {
(Some(&0), Ordering::Greater) => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path must be no longer than libc::sockaddr_un.sun_path",
));
}
(_, Ordering::Greater) | (_, Ordering::Equal) => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path must be shorter than libc::sockaddr_un.sun_path",
));
}
_ => {}
}
for (dst, src) in sockaddr.sun_path.iter_mut().zip(bytes.iter()) {
*dst = *src as libc::c_char;
}
let offset = path_offset(&sockaddr);
let mut socklen = offset + bytes.len();
match bytes.first() {
Some(&0) | None => {}
Some(_) => socklen += 1,
}
Ok((sockaddr, socklen as libc::socklen_t))
}
pub(crate) fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)>
where
T: FromRawFd,
{
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
let mut fds = [-1; 2];
crate::syscall!(socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?;
let pair = unsafe { (T::from_raw_fd(fds[0]), T::from_raw_fd(fds[1])) };
Ok(pair)
}
pub(crate) fn local_addr(socket: RawFd) -> io::Result<SocketAddr> {
SocketAddr::new(|sockaddr, socklen| crate::syscall!(getsockname(socket, sockaddr, socklen)))
}
pub(crate) fn peer_addr(socket: RawFd) -> io::Result<SocketAddr> {
SocketAddr::new(|sockaddr, socklen| crate::syscall!(getpeername(socket, sockaddr, socklen)))
}