| 1 | use crate::ffi::OsStr; |
| 2 | #[cfg (any(doc, target_os = "android" , target_os = "linux" ))] |
| 3 | use crate::os::net::linux_ext; |
| 4 | use crate::os::unix::ffi::OsStrExt; |
| 5 | use crate::path::Path; |
| 6 | use crate::sealed::Sealed; |
| 7 | use crate::sys::cvt; |
| 8 | use crate::{fmt, io, mem, ptr}; |
| 9 | |
| 10 | // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? |
| 11 | #[cfg (not(unix))] |
| 12 | #[allow (non_camel_case_types)] |
| 13 | mod libc { |
| 14 | pub use core::ffi::c_int; |
| 15 | pub type socklen_t = u32; |
| 16 | pub struct sockaddr; |
| 17 | #[derive (Clone)] |
| 18 | pub struct sockaddr_un { |
| 19 | pub sun_path: [u8; 1], |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | const SUN_PATH_OFFSET: usize = mem::offset_of!(libc::sockaddr_un, sun_path); |
| 24 | |
| 25 | pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { |
| 26 | // SAFETY: All zeros is a valid representation for `sockaddr_un`. |
| 27 | let mut addr: libc::sockaddr_un = unsafe { mem::zeroed() }; |
| 28 | addr.sun_family = libc::AF_UNIX as libc::sa_family_t; |
| 29 | |
| 30 | let bytes = path.as_os_str().as_bytes(); |
| 31 | |
| 32 | if bytes.contains(&0) { |
| 33 | return Err(io::const_error!( |
| 34 | io::ErrorKind::InvalidInput, |
| 35 | "paths must not contain interior null bytes" , |
| 36 | )); |
| 37 | } |
| 38 | |
| 39 | if bytes.len() >= addr.sun_path.len() { |
| 40 | return Err(io::const_error!( |
| 41 | io::ErrorKind::InvalidInput, |
| 42 | "path must be shorter than SUN_LEN" , |
| 43 | )); |
| 44 | } |
| 45 | // SAFETY: `bytes` and `addr.sun_path` are not overlapping and |
| 46 | // both point to valid memory. |
| 47 | // NOTE: We zeroed the memory above, so the path is already null |
| 48 | // terminated. |
| 49 | unsafe { |
| 50 | ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len()) |
| 51 | }; |
| 52 | |
| 53 | let mut len = SUN_PATH_OFFSET + bytes.len(); |
| 54 | match bytes.get(0) { |
| 55 | Some(&0) | None => {} |
| 56 | Some(_) => len += 1, |
| 57 | } |
| 58 | Ok((addr, len as libc::socklen_t)) |
| 59 | } |
| 60 | |
| 61 | enum AddressKind<'a> { |
| 62 | Unnamed, |
| 63 | Pathname(&'a Path), |
| 64 | Abstract(&'a [u8]), |
| 65 | } |
| 66 | |
| 67 | /// An address associated with a Unix socket. |
| 68 | /// |
| 69 | /// # Examples |
| 70 | /// |
| 71 | /// ``` |
| 72 | /// use std::os::unix::net::UnixListener; |
| 73 | /// |
| 74 | /// let socket = match UnixListener::bind("/tmp/sock" ) { |
| 75 | /// Ok(sock) => sock, |
| 76 | /// Err(e) => { |
| 77 | /// println!("Couldn't bind: {e:?}" ); |
| 78 | /// return |
| 79 | /// } |
| 80 | /// }; |
| 81 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
| 82 | /// ``` |
| 83 | #[derive (Clone)] |
| 84 | #[stable (feature = "unix_socket" , since = "1.10.0" )] |
| 85 | pub struct SocketAddr { |
| 86 | pub(super) addr: libc::sockaddr_un, |
| 87 | pub(super) len: libc::socklen_t, |
| 88 | } |
| 89 | |
| 90 | impl SocketAddr { |
| 91 | pub(super) fn new<F>(f: F) -> io::Result<SocketAddr> |
| 92 | where |
| 93 | F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, |
| 94 | { |
| 95 | unsafe { |
| 96 | let mut addr: libc::sockaddr_un = mem::zeroed(); |
| 97 | let mut len = size_of::<libc::sockaddr_un>() as libc::socklen_t; |
| 98 | cvt(f((&raw mut addr) as *mut _, &mut len))?; |
| 99 | SocketAddr::from_parts(addr, len) |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | pub(super) fn from_parts( |
| 104 | addr: libc::sockaddr_un, |
| 105 | mut len: libc::socklen_t, |
| 106 | ) -> io::Result<SocketAddr> { |
| 107 | if cfg!(target_os = "openbsd" ) { |
| 108 | // on OpenBSD, getsockname(2) returns the actual size of the socket address, |
| 109 | // and not the len of the content. Figure out the length for ourselves. |
| 110 | // https://marc.info/?l=openbsd-bugs&m=170105481926736&w=2 |
| 111 | let sun_path: &[u8] = |
| 112 | unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&addr.sun_path) }; |
| 113 | len = core::slice::memchr::memchr(0, sun_path) |
| 114 | .map_or(len, |new_len| (new_len + SUN_PATH_OFFSET) as libc::socklen_t); |
| 115 | } |
| 116 | |
| 117 | if len == 0 { |
| 118 | // When there is a datagram from unnamed unix socket |
| 119 | // linux returns zero bytes of address |
| 120 | len = SUN_PATH_OFFSET as libc::socklen_t; // i.e., zero-length address |
| 121 | } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { |
| 122 | return Err(io::const_error!( |
| 123 | io::ErrorKind::InvalidInput, |
| 124 | "file descriptor did not correspond to a Unix socket" , |
| 125 | )); |
| 126 | } |
| 127 | |
| 128 | Ok(SocketAddr { addr, len }) |
| 129 | } |
| 130 | |
| 131 | /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. |
| 132 | /// |
| 133 | /// # Errors |
| 134 | /// |
| 135 | /// Returns an error if the path is longer than `SUN_LEN` or if it contains |
| 136 | /// NULL bytes. |
| 137 | /// |
| 138 | /// # Examples |
| 139 | /// |
| 140 | /// ``` |
| 141 | /// use std::os::unix::net::SocketAddr; |
| 142 | /// use std::path::Path; |
| 143 | /// |
| 144 | /// # fn main() -> std::io::Result<()> { |
| 145 | /// let address = SocketAddr::from_pathname("/path/to/socket" )?; |
| 146 | /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket" ))); |
| 147 | /// # Ok(()) |
| 148 | /// # } |
| 149 | /// ``` |
| 150 | /// |
| 151 | /// Creating a `SocketAddr` with a NULL byte results in an error. |
| 152 | /// |
| 153 | /// ``` |
| 154 | /// use std::os::unix::net::SocketAddr; |
| 155 | /// |
| 156 | /// assert!(SocketAddr::from_pathname("/path/with/ \0/bytes" ).is_err()); |
| 157 | /// ``` |
| 158 | #[stable (feature = "unix_socket_creation" , since = "1.61.0" )] |
| 159 | pub fn from_pathname<P>(path: P) -> io::Result<SocketAddr> |
| 160 | where |
| 161 | P: AsRef<Path>, |
| 162 | { |
| 163 | sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len }) |
| 164 | } |
| 165 | |
| 166 | /// Returns `true` if the address is unnamed. |
| 167 | /// |
| 168 | /// # Examples |
| 169 | /// |
| 170 | /// A named address: |
| 171 | /// |
| 172 | /// ```no_run |
| 173 | /// use std::os::unix::net::UnixListener; |
| 174 | /// |
| 175 | /// fn main() -> std::io::Result<()> { |
| 176 | /// let socket = UnixListener::bind("/tmp/sock" )?; |
| 177 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
| 178 | /// assert_eq!(addr.is_unnamed(), false); |
| 179 | /// Ok(()) |
| 180 | /// } |
| 181 | /// ``` |
| 182 | /// |
| 183 | /// An unnamed address: |
| 184 | /// |
| 185 | /// ``` |
| 186 | /// use std::os::unix::net::UnixDatagram; |
| 187 | /// |
| 188 | /// fn main() -> std::io::Result<()> { |
| 189 | /// let socket = UnixDatagram::unbound()?; |
| 190 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
| 191 | /// assert_eq!(addr.is_unnamed(), true); |
| 192 | /// Ok(()) |
| 193 | /// } |
| 194 | /// ``` |
| 195 | #[must_use ] |
| 196 | #[stable (feature = "unix_socket" , since = "1.10.0" )] |
| 197 | pub fn is_unnamed(&self) -> bool { |
| 198 | matches!(self.address(), AddressKind::Unnamed) |
| 199 | } |
| 200 | |
| 201 | /// Returns the contents of this address if it is a `pathname` address. |
| 202 | /// |
| 203 | /// # Examples |
| 204 | /// |
| 205 | /// With a pathname: |
| 206 | /// |
| 207 | /// ```no_run |
| 208 | /// use std::os::unix::net::UnixListener; |
| 209 | /// use std::path::Path; |
| 210 | /// |
| 211 | /// fn main() -> std::io::Result<()> { |
| 212 | /// let socket = UnixListener::bind("/tmp/sock" )?; |
| 213 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
| 214 | /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock" ))); |
| 215 | /// Ok(()) |
| 216 | /// } |
| 217 | /// ``` |
| 218 | /// |
| 219 | /// Without a pathname: |
| 220 | /// |
| 221 | /// ``` |
| 222 | /// use std::os::unix::net::UnixDatagram; |
| 223 | /// |
| 224 | /// fn main() -> std::io::Result<()> { |
| 225 | /// let socket = UnixDatagram::unbound()?; |
| 226 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
| 227 | /// assert_eq!(addr.as_pathname(), None); |
| 228 | /// Ok(()) |
| 229 | /// } |
| 230 | /// ``` |
| 231 | #[stable (feature = "unix_socket" , since = "1.10.0" )] |
| 232 | #[must_use ] |
| 233 | pub fn as_pathname(&self) -> Option<&Path> { |
| 234 | if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } |
| 235 | } |
| 236 | |
| 237 | fn address(&self) -> AddressKind<'_> { |
| 238 | let len = self.len as usize - SUN_PATH_OFFSET; |
| 239 | let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; |
| 240 | |
| 241 | // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses |
| 242 | if len == 0 |
| 243 | || (cfg!(not(any(target_os = "linux" , target_os = "android" ))) |
| 244 | && self.addr.sun_path[0] == 0) |
| 245 | { |
| 246 | AddressKind::Unnamed |
| 247 | } else if self.addr.sun_path[0] == 0 { |
| 248 | AddressKind::Abstract(&path[1..len]) |
| 249 | } else { |
| 250 | AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) |
| 251 | } |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | #[stable (feature = "unix_socket_abstract" , since = "1.70.0" )] |
| 256 | impl Sealed for SocketAddr {} |
| 257 | |
| 258 | #[doc (cfg(any(target_os = "android" , target_os = "linux" )))] |
| 259 | #[cfg (any(doc, target_os = "android" , target_os = "linux" ))] |
| 260 | #[stable (feature = "unix_socket_abstract" , since = "1.70.0" )] |
| 261 | impl linux_ext::addr::SocketAddrExt for SocketAddr { |
| 262 | fn as_abstract_name(&self) -> Option<&[u8]> { |
| 263 | if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } |
| 264 | } |
| 265 | |
| 266 | fn from_abstract_name<N>(name: N) -> crate::io::Result<Self> |
| 267 | where |
| 268 | N: AsRef<[u8]>, |
| 269 | { |
| 270 | let name = name.as_ref(); |
| 271 | unsafe { |
| 272 | let mut addr: libc::sockaddr_un = mem::zeroed(); |
| 273 | addr.sun_family = libc::AF_UNIX as libc::sa_family_t; |
| 274 | |
| 275 | if name.len() + 1 > addr.sun_path.len() { |
| 276 | return Err(io::const_error!( |
| 277 | io::ErrorKind::InvalidInput, |
| 278 | "abstract socket name must be shorter than SUN_LEN" , |
| 279 | )); |
| 280 | } |
| 281 | |
| 282 | crate::ptr::copy_nonoverlapping( |
| 283 | name.as_ptr(), |
| 284 | addr.sun_path.as_mut_ptr().add(1) as *mut u8, |
| 285 | name.len(), |
| 286 | ); |
| 287 | let len = (SUN_PATH_OFFSET + 1 + name.len()) as libc::socklen_t; |
| 288 | SocketAddr::from_parts(addr, len) |
| 289 | } |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | #[stable (feature = "unix_socket" , since = "1.10.0" )] |
| 294 | impl fmt::Debug for SocketAddr { |
| 295 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 296 | match self.address() { |
| 297 | AddressKind::Unnamed => write!(fmt, "(unnamed)" ), |
| 298 | AddressKind::Abstract(name: &[u8]) => write!(fmt, " \"{}\" (abstract)" , name.escape_ascii()), |
| 299 | AddressKind::Pathname(path: &Path) => write!(fmt, " {path:?} (pathname)" ), |
| 300 | } |
| 301 | } |
| 302 | } |
| 303 | |