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 { |
29 | fn address(&self) -> AddressKind<'_> { |
30 | let offset: usize = 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: usize = self.socklen as usize - offset; |
36 | let path: &[u8] = 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 | pub fn is_unnamed(&self) -> bool { |
82 | matches!(self.address(), AddressKind::Unnamed) |
83 | } |
84 | |
85 | /// Returns the contents of this address if it is a `pathname` address. |
86 | /// |
87 | /// Documentation reflected in [`SocketAddr`] |
88 | /// |
89 | /// [`SocketAddr`]: std::os::unix::net::SocketAddr |
90 | pub fn as_pathname(&self) -> Option<&Path> { |
91 | if let AddressKind::Pathname(path) = self.address() { |
92 | Some(path) |
93 | } else { |
94 | None |
95 | } |
96 | } |
97 | |
98 | /// Returns the contents of this address if it is an abstract namespace |
99 | /// without the leading null byte. |
100 | // Link to std::os::unix::net::SocketAddr pending |
101 | // https://github.com/rust-lang/rust/issues/85410. |
102 | pub fn as_abstract_namespace(&self) -> Option<&[u8]> { |
103 | if let AddressKind::Abstract(path) = self.address() { |
104 | Some(path) |
105 | } else { |
106 | None |
107 | } |
108 | } |
109 | } |
110 | } |
111 | |
112 | impl fmt::Debug for SocketAddr { |
113 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
114 | match self.address() { |
115 | AddressKind::Unnamed => write!(fmt, "(unnamed)" ), |
116 | AddressKind::Abstract(name: &[u8]) => write!(fmt, " {} (abstract)" , AsciiEscaped(name)), |
117 | AddressKind::Pathname(path: &Path) => write!(fmt, " {:?} (pathname)" , path), |
118 | } |
119 | } |
120 | } |
121 | |
122 | impl<'a> fmt::Display for AsciiEscaped<'a> { |
123 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
124 | write!(fmt, " \"" )?; |
125 | for byte: u8 in self.0.iter().cloned().flat_map(ascii::escape_default) { |
126 | write!(fmt, " {}" , byte as char)?; |
127 | } |
128 | write!(fmt, " \"" ) |
129 | } |
130 | } |
131 | |