mod socketaddr; pub use self::socketaddr::SocketAddr; /// Get the `sun_path` field offset of `sockaddr_un` for the target OS. /// /// On Linux, this funtion equates to the same value as /// `size_of::()`, but some other implementations include /// other fields before `sun_path`, so the expression more portably /// describes the size of the address structure. pub(in crate::sys) 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 } cfg_os_poll! { use std::cmp::Ordering; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::{RawFd, FromRawFd}; use std::path::Path; use std::{io, mem}; pub(crate) mod datagram; pub(crate) mod listener; pub(crate) mod stream; pub(in crate::sys) fn socket_addr(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { let sockaddr = mem::MaybeUninit::::zeroed(); // This is safe to assume because a `libc::sockaddr_un` filled with `0` // bytes is properly initialized. // // `0` is a valid value for `sockaddr_un::sun_family`; it is // `libc::AF_UNSPEC`. // // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an // abstract path. 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.get(0), bytes.len().cmp(&sockaddr.sun_path.len())) { // Abstract paths don't need a null terminator (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.get(0) { // The struct has already been zeroes so the null byte for pathname // addresses is already there. Some(&0) | None => {} Some(_) => socklen += 1, } Ok((sockaddr, socklen as libc::socklen_t)) } fn pair(flags: libc::c_int) -> io::Result<(T, T)> where T: FromRawFd, { #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "solaris")))] 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])) }; // Darwin and Solaris do not have SOCK_NONBLOCK or SOCK_CLOEXEC. // // In order to set those flags, additional `fcntl` sys calls must be // performed. If a `fnctl` fails after the sockets have been created, // the file descriptors will leak. Creating `pair` above ensures that if // there is an error, the file descriptors are closed. #[cfg(any(target_os = "ios", target_os = "macos", target_os = "solaris"))] { syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?; syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?; syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?; syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?; } Ok(pair) } // The following functions can't simply be replaced with a call to // `net::UnixDatagram` because of our `SocketAddr` type. fn local_addr(socket: RawFd) -> io::Result { SocketAddr::new(|sockaddr, socklen| syscall!(getsockname(socket, sockaddr, socklen))) } fn peer_addr(socket: RawFd) -> io::Result { SocketAddr::new(|sockaddr, socklen| syscall!(getpeername(socket, sockaddr, socklen))) } #[cfg(test)] mod tests { use super::{path_offset, socket_addr}; use std::path::Path; use std::str; #[test] fn pathname_address() { const PATH: &str = "./foo/bar.txt"; const PATH_LEN: usize = 13; // Pathname addresses do have a null terminator, so `socklen` is // expected to be `PATH_LEN` + `offset` + 1. let path = Path::new(PATH); let (sockaddr, actual) = socket_addr(path).unwrap(); let offset = path_offset(&sockaddr); let expected = PATH_LEN + offset + 1; assert_eq!(expected as libc::socklen_t, actual) } #[test] fn abstract_address() { const PATH: &[u8] = &[0, 116, 111, 107, 105, 111]; const PATH_LEN: usize = 6; // Abstract addresses do not have a null terminator, so `socklen` is // expected to be `PATH_LEN` + `offset`. let abstract_path = str::from_utf8(PATH).unwrap(); let path = Path::new(abstract_path); let (sockaddr, actual) = socket_addr(path).unwrap(); let offset = path_offset(&sockaddr); let expected = PATH_LEN + offset; assert_eq!(expected as libc::socklen_t, actual) } } }