| 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::<sa_family_t>()`, 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::<libc::sockaddr_un>::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.first(), 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.first() { |
| // 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<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]; |
| 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 doesn't 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"))] |
| { |
| 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> { |
| SocketAddr::new(|sockaddr, socklen| syscall!(getsockname(socket, sockaddr, socklen))) |
| } |
| |
| fn peer_addr(socket: RawFd) -> io::Result<SocketAddr> { |
| 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) |
| } |
| } |
| } |