1 use super::path_offset; 2 use std::ffi::OsStr; 3 use std::os::unix::ffi::OsStrExt; 4 use std::path::Path; 5 use std::{ascii, fmt}; 6 7 /// An address associated with a `mio` specific Unix socket. 8 /// 9 /// This is implemented instead of imported from [`net::SocketAddr`] because 10 /// there is no way to create a [`net::SocketAddr`]. One must be returned by 11 /// [`accept`], so this is returned instead. 12 /// 13 /// [`net::SocketAddr`]: std::os::unix::net::SocketAddr 14 /// [`accept`]: #method.accept 15 pub struct SocketAddr { 16 sockaddr: libc::sockaddr_un, 17 socklen: libc::socklen_t, 18 } 19 20 struct AsciiEscaped<'a>(&'a [u8]); 21 22 enum AddressKind<'a> { 23 Unnamed, 24 Pathname(&'a Path), 25 Abstract(&'a [u8]), 26 } 27 28 impl SocketAddr { address(&self) -> AddressKind<'_>29 fn address(&self) -> AddressKind<'_> { 30 let offset = path_offset(&self.sockaddr); 31 // Don't underflow in `len` below. 32 if (self.socklen as usize) < offset { 33 return AddressKind::Unnamed; 34 } 35 let len = self.socklen as usize - offset; 36 let path = unsafe { &*(&self.sockaddr.sun_path as *const [libc::c_char] as *const [u8]) }; 37 38 // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses 39 if len == 0 40 || (cfg!(not(any(target_os = "linux", target_os = "android"))) 41 && self.sockaddr.sun_path[0] == 0) 42 { 43 AddressKind::Unnamed 44 } else if self.sockaddr.sun_path[0] == 0 { 45 AddressKind::Abstract(&path[1..len]) 46 } else { 47 AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) 48 } 49 } 50 } 51 52 cfg_os_poll! { 53 use std::{io, mem}; 54 55 impl SocketAddr { 56 pub(crate) fn new<F>(f: F) -> io::Result<SocketAddr> 57 where 58 F: FnOnce(*mut libc::sockaddr, &mut libc::socklen_t) -> io::Result<libc::c_int>, 59 { 60 let mut sockaddr = { 61 let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed(); 62 unsafe { sockaddr.assume_init() } 63 }; 64 65 let raw_sockaddr = &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr; 66 let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t; 67 68 f(raw_sockaddr, &mut socklen)?; 69 Ok(SocketAddr::from_parts(sockaddr, socklen)) 70 } 71 72 pub(crate) fn from_parts(sockaddr: libc::sockaddr_un, socklen: libc::socklen_t) -> SocketAddr { 73 SocketAddr { sockaddr, socklen } 74 } 75 76 /// Returns `true` if the address is unnamed. 77 /// 78 /// Documentation reflected in [`SocketAddr`] 79 /// 80 /// [`SocketAddr`]: std::os::unix::net::SocketAddr 81 // FIXME: The matches macro requires rust 1.42.0 and we still support 1.39.0 82 #[allow(clippy::match_like_matches_macro)] 83 pub fn is_unnamed(&self) -> bool { 84 if let AddressKind::Unnamed = self.address() { 85 true 86 } else { 87 false 88 } 89 } 90 91 /// Returns the contents of this address if it is a `pathname` address. 92 /// 93 /// Documentation reflected in [`SocketAddr`] 94 /// 95 /// [`SocketAddr`]: std::os::unix::net::SocketAddr 96 pub fn as_pathname(&self) -> Option<&Path> { 97 if let AddressKind::Pathname(path) = self.address() { 98 Some(path) 99 } else { 100 None 101 } 102 } 103 } 104 } 105 106 impl fmt::Debug for SocketAddr { fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result107 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 108 match self.address() { 109 AddressKind::Unnamed => write!(fmt, "(unnamed)"), 110 AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)), 111 AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path), 112 } 113 } 114 } 115 116 impl<'a> fmt::Display for AsciiEscaped<'a> { fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result117 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 118 write!(fmt, "\"")?; 119 for byte in self.0.iter().cloned().flat_map(ascii::escape_default) { 120 write!(fmt, "{}", byte as char)?; 121 } 122 write!(fmt, "\"") 123 } 124 } 125