1use crate::byte_str::ByteStr;
2use crate::ffi::OsStr;
3use crate::macros::rustdoc_skip_body;
4#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))]
5use crate::os::net::linux_ext;
6use crate::os::unix::ffi::OsStrExt;
7use crate::path::Path;
8use crate::sys::cvt;
9use crate::{fmt, io, mem, ptr};
10
11#[cfg(not(unix))]
13#[allow(non_camel_case_types)]
14mod 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
24const SUN_PATH_OFFSET: usize = mem::offset_of!(libc::sockaddr_un, sun_path);
25
26#[rustdoc_skip_body]
27pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
28 let mut addr: libc::sockaddr_un = unsafe { mem::zeroed() };
30 addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
31
32 let bytes = path.as_os_str().as_bytes();
33
34 if bytes.contains(&0) {
35 return Err(io::const_error!(
36 io::ErrorKind::InvalidInput,
37 "paths must not contain interior null bytes",
38 ));
39 }
40
41 if bytes.len() >= addr.sun_path.len() {
42 return Err(io::const_error!(
43 io::ErrorKind::InvalidInput,
44 "path must be shorter than SUN_LEN",
45 ));
46 }
47 unsafe {
52 ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len())
53 };
54
55 let mut len = SUN_PATH_OFFSET + bytes.len();
56 match bytes.get(0) {
57 Some(&0) | None => {}
58 Some(_) => {
59 if cfg!(not(any(target_os = "qnx", target_env = "nto71"))) {
64 len += 1
65 }
66 }
67 }
68 Ok((addr, len as libc::socklen_t))
69}
70
71enum AddressKind<'a> {
72 Unnamed,
73 Pathname(&'a Path),
74 Abstract(&'a ByteStr),
75}
76
77#[cfg_attr(target_family = "unix", doc = "```")]
82#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")]
83#[derive(Clone)]
95#[stable(feature = "unix_socket", since = "1.10.0")]
96pub struct SocketAddr {
97 pub(super) addr: libc::sockaddr_un,
98 pub(super) len: libc::socklen_t,
99}
100
101impl SocketAddr {
102 pub(super) fn new<F>(f: F) -> io::Result<SocketAddr>
103 where
104 F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int,
105 {
106 unsafe {
107 let mut addr: libc::sockaddr_un = mem::zeroed();
108 let mut len = size_of::<libc::sockaddr_un>() as libc::socklen_t;
109 cvt(f((&raw mut addr) as *mut _, &mut len))?;
110 SocketAddr::from_parts(addr, len)
111 }
112 }
113
114 #[rustdoc_skip_body]
115 pub(super) fn from_parts(
116 addr: libc::sockaddr_un,
117 mut len: libc::socklen_t,
118 ) -> io::Result<SocketAddr> {
119 if cfg!(target_os = "openbsd") {
120 let sun_path: &[u8] =
124 unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&addr.sun_path) };
125 len = core::slice::memchr::memchr(0, sun_path)
126 .map_or(len, |new_len| (new_len + SUN_PATH_OFFSET) as libc::socklen_t);
127 }
128
129 len = len.min(size_of::<libc::sockaddr_un>() as libc::socklen_t);
130
131 if len == 0 {
132 len = SUN_PATH_OFFSET as libc::socklen_t; } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t {
136 return Err(io::const_error!(
137 io::ErrorKind::InvalidInput,
138 "file descriptor did not correspond to a Unix socket",
139 ));
140 }
141
142 Ok(SocketAddr { addr, len })
143 }
144
145 #[cfg_attr(target_family = "unix", doc = "```")]
155 #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")]
156 #[cfg_attr(target_family = "unix", doc = "```")]
169 #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")]
170 #[stable(feature = "unix_socket_creation", since = "1.61.0")]
175 pub fn from_pathname<P>(path: P) -> io::Result<SocketAddr>
176 where
177 P: AsRef<Path>,
178 {
179 sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len })
180 }
181
182 #[cfg_attr(target_family = "unix", doc = "```no_run")]
189 #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")]
190 #[cfg_attr(target_family = "unix", doc = "```")]
203 #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")]
204 #[must_use]
214 #[stable(feature = "unix_socket", since = "1.10.0")]
215 pub fn is_unnamed(&self) -> bool {
216 matches!(self.address(), AddressKind::Unnamed)
217 }
218
219 #[cfg_attr(target_family = "unix", doc = "```no_run")]
226 #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")]
227 #[cfg_attr(target_family = "unix", doc = "```")]
241 #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")]
242 #[stable(feature = "unix_socket", since = "1.10.0")]
252 #[must_use]
253 pub fn as_pathname(&self) -> Option<&Path> {
254 if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None }
255 }
256
257 #[rustdoc_skip_body]
258 fn address(&self) -> AddressKind<'_> {
259 let len = self.len as usize - SUN_PATH_OFFSET;
260 let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) };
261
262 if len == 0
264 || (cfg!(not(any(target_os = "linux", target_os = "android", target_os = "cygwin")))
265 && self.addr.sun_path[0] == 0)
266 {
267 AddressKind::Unnamed
268 } else if self.addr.sun_path[0] == 0 {
269 AddressKind::Abstract(ByteStr::from_bytes(&path[1..len]))
270 } else {
271 let end = core::slice::memchr::memchr(0, &path[..len]).unwrap_or(len);
275 AddressKind::Pathname(OsStr::from_bytes(&path[..end]).as_ref())
276 }
277 }
278}
279
280#[doc(cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin")))]
281#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))]
282#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
283impl linux_ext::addr::SocketAddrExt for SocketAddr {
284 fn as_abstract_name(&self) -> Option<&[u8]> {
285 if let AddressKind::Abstract(name) = self.address() { Some(name.as_bytes()) } else { None }
286 }
287
288 #[rustdoc_skip_body]
289 fn from_abstract_name<N>(name: N) -> io::Result<Self>
290 where
291 N: AsRef<[u8]>,
292 {
293 let name = name.as_ref();
294 unsafe {
295 let mut addr: libc::sockaddr_un = mem::zeroed();
296 addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
297
298 if name.len() + 1 > addr.sun_path.len() {
299 return Err(io::const_error!(
300 io::ErrorKind::InvalidInput,
301 "abstract socket name must be shorter than SUN_LEN",
302 ));
303 }
304
305 crate::ptr::copy_nonoverlapping(
306 name.as_ptr(),
307 addr.sun_path.as_mut_ptr().add(1) as *mut u8,
308 name.len(),
309 );
310 let len = (SUN_PATH_OFFSET + 1 + name.len()) as libc::socklen_t;
311 SocketAddr::from_parts(addr, len)
312 }
313 }
314}
315
316#[stable(feature = "unix_socket", since = "1.10.0")]
317impl fmt::Debug for SocketAddr {
318 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
319 match self.address() {
320 AddressKind::Unnamed => write!(fmt, "(unnamed)"),
321 AddressKind::Abstract(name) => write!(fmt, "{name:?} (abstract)"),
322 AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"),
323 }
324 }
325}