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