1use crate::bstr::ByteStr;
2use crate::ffi::OsStr;
3#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))]
4use crate::os::net::linux_ext;
5use crate::os::unix::ffi::OsStrExt;
6use crate::path::Path;
7use crate::sys::cvt;
8use crate::{fmt, io, mem, ptr};
9
10#[cfg(not(unix))]
12#[allow(non_camel_case_types)]
13mod 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
23const SUN_PATH_OFFSET: usize = mem::offset_of!(libc::sockaddr_un, sun_path);
24
25pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
26 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 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(_) => {
57 if cfg!(not(any(target_env = "nto80", target_env = "nto71"))) {
62 len += 1
63 }
64 }
65 }
66 Ok((addr, len as libc::socklen_t))
67}
68
69enum AddressKind<'a> {
70 Unnamed,
71 Pathname(&'a Path),
72 Abstract(&'a ByteStr),
73}
74
75#[derive(Clone)]
92#[stable(feature = "unix_socket", since = "1.10.0")]
93pub struct SocketAddr {
94 pub(super) addr: libc::sockaddr_un,
95 pub(super) len: libc::socklen_t,
96}
97
98impl SocketAddr {
99 pub(super) fn new<F>(f: F) -> io::Result<SocketAddr>
100 where
101 F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int,
102 {
103 unsafe {
104 let mut addr: libc::sockaddr_un = mem::zeroed();
105 let mut len = size_of::<libc::sockaddr_un>() as libc::socklen_t;
106 cvt(f((&raw mut addr) as *mut _, &mut len))?;
107 SocketAddr::from_parts(addr, len)
108 }
109 }
110
111 pub(super) fn from_parts(
112 addr: libc::sockaddr_un,
113 mut len: libc::socklen_t,
114 ) -> io::Result<SocketAddr> {
115 if cfg!(target_os = "openbsd") {
116 let sun_path: &[u8] =
120 unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&addr.sun_path) };
121 len = core::slice::memchr::memchr(0, sun_path)
122 .map_or(len, |new_len| (new_len + SUN_PATH_OFFSET) as libc::socklen_t);
123 }
124
125 if len == 0 {
126 len = SUN_PATH_OFFSET as libc::socklen_t; } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t {
130 return Err(io::const_error!(
131 io::ErrorKind::InvalidInput,
132 "file descriptor did not correspond to a Unix socket",
133 ));
134 }
135
136 Ok(SocketAddr { addr, len })
137 }
138
139 #[stable(feature = "unix_socket_creation", since = "1.61.0")]
167 pub fn from_pathname<P>(path: P) -> io::Result<SocketAddr>
168 where
169 P: AsRef<Path>,
170 {
171 sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len })
172 }
173
174 #[must_use]
204 #[stable(feature = "unix_socket", since = "1.10.0")]
205 pub fn is_unnamed(&self) -> bool {
206 matches!(self.address(), AddressKind::Unnamed)
207 }
208
209 #[stable(feature = "unix_socket", since = "1.10.0")]
240 #[must_use]
241 pub fn as_pathname(&self) -> Option<&Path> {
242 if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None }
243 }
244
245 fn address(&self) -> AddressKind<'_> {
246 let len = self.len as usize - SUN_PATH_OFFSET;
247 let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) };
248
249 if len == 0
251 || (cfg!(not(any(target_os = "linux", target_os = "android", target_os = "cygwin")))
252 && self.addr.sun_path[0] == 0)
253 {
254 AddressKind::Unnamed
255 } else if self.addr.sun_path[0] == 0 {
256 AddressKind::Abstract(ByteStr::from_bytes(&path[1..len]))
257 } else {
258 let end =
263 if cfg!(any(target_env = "nto80", target_env = "nto71")) { len } else { len - 1 };
264 AddressKind::Pathname(OsStr::from_bytes(&path[..end]).as_ref())
265 }
266 }
267}
268
269#[doc(cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin")))]
270#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))]
271#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
272impl linux_ext::addr::SocketAddrExt for SocketAddr {
273 fn as_abstract_name(&self) -> Option<&[u8]> {
274 if let AddressKind::Abstract(name) = self.address() { Some(name.as_bytes()) } else { None }
275 }
276
277 fn from_abstract_name<N>(name: N) -> io::Result<Self>
278 where
279 N: AsRef<[u8]>,
280 {
281 let name = name.as_ref();
282 unsafe {
283 let mut addr: libc::sockaddr_un = mem::zeroed();
284 addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
285
286 if name.len() + 1 > addr.sun_path.len() {
287 return Err(io::const_error!(
288 io::ErrorKind::InvalidInput,
289 "abstract socket name must be shorter than SUN_LEN",
290 ));
291 }
292
293 crate::ptr::copy_nonoverlapping(
294 name.as_ptr(),
295 addr.sun_path.as_mut_ptr().add(1) as *mut u8,
296 name.len(),
297 );
298 let len = (SUN_PATH_OFFSET + 1 + name.len()) as libc::socklen_t;
299 SocketAddr::from_parts(addr, len)
300 }
301 }
302}
303
304#[stable(feature = "unix_socket", since = "1.10.0")]
305impl fmt::Debug for SocketAddr {
306 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
307 match self.address() {
308 AddressKind::Unnamed => write!(fmt, "(unnamed)"),
309 AddressKind::Abstract(name) => write!(fmt, "{name:?} (abstract)"),
310 AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"),
311 }
312 }
313}