#[cfg(target_os = "android")]
use std::os::android::net::SocketAddrExt;
#[cfg(target_os = "linux")]
use std::os::linux::net::SocketAddrExt;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::FromRawFd;
use std::os::unix::net::SocketAddr;
use std::{io, mem, ptr};
pub(crate) mod datagram;
pub(crate) mod listener;
pub(crate) mod stream;
const UNNAMED_ADDRESS: &[u8] = &[];
fn path_offset(sockaddr: &libc::sockaddr_un) -> usize {
let base = sockaddr as *const _ as usize;
let path = &sockaddr.sun_path as *const _ as usize;
path - base
}
fn unix_addr(address: &SocketAddr) -> (libc::sockaddr_un, libc::socklen_t) {
let mut sockaddr = unsafe { mem::zeroed::<libc::sockaddr_un>() };
sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
#[allow(unused_mut)] let mut offset = 0;
let addr = match address.as_pathname() {
Some(path) => path.as_os_str().as_bytes(),
#[cfg(any(target_os = "android", target_os = "linux"))]
None => match address.as_abstract_name() {
Some(name) => {
offset += 1;
name
}
None => UNNAMED_ADDRESS,
},
#[cfg(not(any(target_os = "android", target_os = "linux")))]
None => UNNAMED_ADDRESS,
};
debug_assert!(offset + addr.len() <= sockaddr.sun_path.len());
unsafe {
ptr::copy_nonoverlapping(
addr.as_ptr(),
sockaddr.sun_path.as_mut_ptr().add(offset).cast(),
addr.len(),
)
};
let mut addrlen = path_offset(&sockaddr) + addr.len();
match addr.first() {
Some(&0) | None => {}
Some(_) => addrlen += 1,
}
(sockaddr, addrlen as _)
}
fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)>
where
T: FromRawFd,
{
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "nto",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
)))]
let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
let mut fds = [-1; 2];
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])) };
#[cfg(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "nto",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
))]
{
syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?;
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))]
syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?;
syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?;
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))]
syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?;
}
Ok(pair)
}
#[cfg(test)]
mod tests {
use std::os::unix::net::SocketAddr;
use std::path::Path;
use std::str;
use super::{path_offset, unix_addr};
#[test]
fn pathname_address() {
const PATH: &str = "./foo/bar.txt";
const PATH_LEN: usize = 13;
let address = SocketAddr::from_pathname(Path::new(PATH)).unwrap();
let (sockaddr, actual) = unix_addr(&address);
let offset = path_offset(&sockaddr);
let expected = PATH_LEN + offset + 1;
assert_eq!(expected as libc::socklen_t, actual)
}
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn abstract_address() {
use std::os::linux::net::SocketAddrExt;
const PATH: &[u8] = &[0, 116, 111, 107, 105, 111];
const PATH_LEN: usize = 6;
let address = SocketAddr::from_abstract_name(PATH).unwrap();
let (sockaddr, actual) = unix_addr(&address);
let offset = path_offset(&sockaddr);
let expected = PATH_LEN + offset;
assert_eq!(expected as libc::socklen_t, actual)
}
}