use crate::ffi::OsStr; use crate::os::unix::ffi::OsStrExt; use crate::path::Path; use crate::sys::cvt; use crate::{ascii, fmt, io, iter, mem}; // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? #[cfg(not(unix))] #[allow(non_camel_case_types)] mod libc { pub use libc::c_int; pub type socklen_t = u32; pub struct sockaddr; #[derive(Clone)] pub struct sockaddr_un; } fn sun_path_offset(addr: &libc::sockaddr_un) -> usize { // Work with an actual instance of the type since using a null pointer is UB let base = addr as *const _ as usize; let path = &addr.sun_path as *const _ as usize; path - base } pub(super) unsafe fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { let mut addr: libc::sockaddr_un = mem::zeroed(); addr.sun_family = libc::AF_UNIX as libc::sa_family_t; let bytes = path.as_os_str().as_bytes(); if bytes.contains(&0) { return Err(io::Error::new_const( io::ErrorKind::InvalidInput, &"paths must not contain interior null bytes", )); } if bytes.len() >= addr.sun_path.len() { return Err(io::Error::new_const( io::ErrorKind::InvalidInput, &"path must be shorter than SUN_LEN", )); } for (dst, src) in iter::zip(&mut addr.sun_path, bytes) { *dst = *src as libc::c_char; } // null byte for pathname addresses is already there because we zeroed the // struct let mut len = sun_path_offset(&addr) + bytes.len(); match bytes.get(0) { Some(&0) | None => {} Some(_) => len += 1, } Ok((addr, len as libc::socklen_t)) } enum AddressKind<'a> { Unnamed, Pathname(&'a Path), Abstract(&'a [u8]), } struct AsciiEscaped<'a>(&'a [u8]); 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, "\"") } } /// An address associated with a Unix socket. /// /// # Examples /// /// ``` /// use std::os::unix::net::UnixListener; /// /// let socket = match UnixListener::bind("/tmp/sock") { /// Ok(sock) => sock, /// Err(e) => { /// println!("Couldn't bind: {:?}", e); /// return /// } /// }; /// let addr = socket.local_addr().expect("Couldn't get local address"); /// ``` #[derive(Clone)] #[stable(feature = "unix_socket", since = "1.10.0")] pub struct SocketAddr { addr: libc::sockaddr_un, len: libc::socklen_t, } impl SocketAddr { pub(super) fn new(f: F) -> io::Result where F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, { unsafe { let mut addr: libc::sockaddr_un = mem::zeroed(); let mut len = mem::size_of::() as libc::socklen_t; cvt(f(&mut addr as *mut _ as *mut _, &mut len))?; SocketAddr::from_parts(addr, len) } } pub(super) fn from_parts( addr: libc::sockaddr_un, mut len: libc::socklen_t, ) -> io::Result { if len == 0 { // When there is a datagram from unnamed unix socket // linux returns zero bytes of address len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { return Err(io::Error::new_const( io::ErrorKind::InvalidInput, &"file descriptor did not correspond to a Unix socket", )); } Ok(SocketAddr { addr, len }) } /// Returns `true` if the address is unnamed. /// /// # Examples /// /// A named address: /// /// ```no_run /// use std::os::unix::net::UnixListener; /// /// fn main() -> std::io::Result<()> { /// let socket = UnixListener::bind("/tmp/sock")?; /// let addr = socket.local_addr().expect("Couldn't get local address"); /// assert_eq!(addr.is_unnamed(), false); /// Ok(()) /// } /// ``` /// /// An unnamed address: /// /// ``` /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { /// let socket = UnixDatagram::unbound()?; /// let addr = socket.local_addr().expect("Couldn't get local address"); /// assert_eq!(addr.is_unnamed(), true); /// Ok(()) /// } /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn is_unnamed(&self) -> bool { if let AddressKind::Unnamed = self.address() { true } else { false } } /// Returns the contents of this address if it is a `pathname` address. /// /// # Examples /// /// With a pathname: /// /// ```no_run /// use std::os::unix::net::UnixListener; /// use std::path::Path; /// /// fn main() -> std::io::Result<()> { /// let socket = UnixListener::bind("/tmp/sock")?; /// let addr = socket.local_addr().expect("Couldn't get local address"); /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); /// Ok(()) /// } /// ``` /// /// Without a pathname: /// /// ``` /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { /// let socket = UnixDatagram::unbound()?; /// let addr = socket.local_addr().expect("Couldn't get local address"); /// assert_eq!(addr.as_pathname(), None); /// Ok(()) /// } /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn as_pathname(&self) -> Option<&Path> { if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } } fn address(&self) -> AddressKind<'_> { let len = self.len as usize - sun_path_offset(&self.addr); let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses if len == 0 || (cfg!(not(any(target_os = "linux", target_os = "android"))) && self.addr.sun_path[0] == 0) { AddressKind::Unnamed } else if self.addr.sun_path[0] == 0 { AddressKind::Abstract(&path[1..len]) } else { AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) } } } #[stable(feature = "unix_socket", since = "1.10.0")] 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), } } }