]>
Commit | Line | Data |
---|---|---|
fc512014 XL |
1 | use crate::ffi::OsStr; |
2 | use crate::os::unix::ffi::OsStrExt; | |
3 | use crate::path::Path; | |
4 | use crate::sys::cvt; | |
5099ac24 | 5 | use crate::{ascii, fmt, io, mem, ptr}; |
fc512014 XL |
6 | |
7 | // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? | |
8 | #[cfg(not(unix))] | |
9 | #[allow(non_camel_case_types)] | |
10 | mod libc { | |
11 | pub use libc::c_int; | |
12 | pub type socklen_t = u32; | |
13 | pub struct sockaddr; | |
14 | #[derive(Clone)] | |
15 | pub struct sockaddr_un; | |
16 | } | |
17 | ||
18 | fn sun_path_offset(addr: &libc::sockaddr_un) -> usize { | |
19 | // Work with an actual instance of the type since using a null pointer is UB | |
ee023bcb FG |
20 | let base = (addr as *const libc::sockaddr_un).addr(); |
21 | let path = (&addr.sun_path as *const libc::c_char).addr(); | |
fc512014 XL |
22 | path - base |
23 | } | |
24 | ||
5099ac24 FG |
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() }; | |
fc512014 XL |
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) { | |
5099ac24 | 33 | return Err(io::const_io_error!( |
fc512014 | 34 | io::ErrorKind::InvalidInput, |
5099ac24 | 35 | "paths must not contain interior null bytes", |
fc512014 XL |
36 | )); |
37 | } | |
38 | ||
39 | if bytes.len() >= addr.sun_path.len() { | |
5099ac24 | 40 | return Err(io::const_io_error!( |
fc512014 | 41 | io::ErrorKind::InvalidInput, |
5099ac24 | 42 | "path must be shorter than SUN_LEN", |
fc512014 XL |
43 | )); |
44 | } | |
5099ac24 FG |
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 | }; | |
fc512014 XL |
52 | |
53 | let mut len = sun_path_offset(&addr) + 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 | struct AsciiEscaped<'a>(&'a [u8]); | |
68 | ||
69 | impl<'a> fmt::Display for AsciiEscaped<'a> { | |
70 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | |
71 | write!(fmt, "\"")?; | |
72 | for byte in self.0.iter().cloned().flat_map(ascii::escape_default) { | |
73 | write!(fmt, "{}", byte as char)?; | |
74 | } | |
75 | write!(fmt, "\"") | |
76 | } | |
77 | } | |
78 | ||
79 | /// An address associated with a Unix socket. | |
80 | /// | |
81 | /// # Examples | |
82 | /// | |
83 | /// ``` | |
84 | /// use std::os::unix::net::UnixListener; | |
85 | /// | |
86 | /// let socket = match UnixListener::bind("/tmp/sock") { | |
87 | /// Ok(sock) => sock, | |
88 | /// Err(e) => { | |
ee023bcb | 89 | /// println!("Couldn't bind: {e:?}"); |
fc512014 XL |
90 | /// return |
91 | /// } | |
92 | /// }; | |
93 | /// let addr = socket.local_addr().expect("Couldn't get local address"); | |
94 | /// ``` | |
95 | #[derive(Clone)] | |
96 | #[stable(feature = "unix_socket", since = "1.10.0")] | |
97 | pub struct SocketAddr { | |
c295e0f8 XL |
98 | pub(super) addr: libc::sockaddr_un, |
99 | pub(super) len: libc::socklen_t, | |
fc512014 XL |
100 | } |
101 | ||
102 | impl SocketAddr { | |
103 | pub(super) fn new<F>(f: F) -> io::Result<SocketAddr> | |
104 | where | |
105 | F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, | |
106 | { | |
107 | unsafe { | |
108 | let mut addr: libc::sockaddr_un = mem::zeroed(); | |
109 | let mut len = mem::size_of::<libc::sockaddr_un>() as libc::socklen_t; | |
110 | cvt(f(&mut addr as *mut _ as *mut _, &mut len))?; | |
111 | SocketAddr::from_parts(addr, len) | |
112 | } | |
113 | } | |
114 | ||
115 | pub(super) fn from_parts( | |
116 | addr: libc::sockaddr_un, | |
117 | mut len: libc::socklen_t, | |
118 | ) -> io::Result<SocketAddr> { | |
119 | if len == 0 { | |
120 | // When there is a datagram from unnamed unix socket | |
121 | // linux returns zero bytes of address | |
122 | len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address | |
123 | } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { | |
5099ac24 | 124 | return Err(io::const_io_error!( |
fc512014 | 125 | io::ErrorKind::InvalidInput, |
5099ac24 | 126 | "file descriptor did not correspond to a Unix socket", |
fc512014 XL |
127 | )); |
128 | } | |
129 | ||
130 | Ok(SocketAddr { addr, len }) | |
131 | } | |
132 | ||
5099ac24 FG |
133 | /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. |
134 | /// | |
135 | /// # Errors | |
136 | /// | |
137 | /// Returns an error if the path is longer than `SUN_LEN` or if it contains | |
138 | /// NULL bytes. | |
139 | /// | |
140 | /// # Examples | |
141 | /// | |
142 | /// ``` | |
5099ac24 FG |
143 | /// use std::os::unix::net::SocketAddr; |
144 | /// use std::path::Path; | |
145 | /// | |
146 | /// # fn main() -> std::io::Result<()> { | |
ee023bcb | 147 | /// let address = SocketAddr::from_pathname("/path/to/socket")?; |
5099ac24 FG |
148 | /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); |
149 | /// # Ok(()) | |
150 | /// # } | |
151 | /// ``` | |
152 | /// | |
153 | /// Creating a `SocketAddr` with a NULL byte results in an error. | |
154 | /// | |
155 | /// ``` | |
5099ac24 FG |
156 | /// use std::os::unix::net::SocketAddr; |
157 | /// | |
ee023bcb | 158 | /// assert!(SocketAddr::from_pathname("/path/with/\0/bytes").is_err()); |
5099ac24 | 159 | /// ``` |
ee023bcb FG |
160 | #[stable(feature = "unix_socket_creation", since = "1.61.0")] |
161 | pub fn from_pathname<P>(path: P) -> io::Result<SocketAddr> | |
5099ac24 FG |
162 | where |
163 | P: AsRef<Path>, | |
164 | { | |
165 | sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len }) | |
166 | } | |
167 | ||
fc512014 XL |
168 | /// Returns `true` if the address is unnamed. |
169 | /// | |
170 | /// # Examples | |
171 | /// | |
172 | /// A named address: | |
173 | /// | |
174 | /// ```no_run | |
175 | /// use std::os::unix::net::UnixListener; | |
176 | /// | |
177 | /// fn main() -> std::io::Result<()> { | |
178 | /// let socket = UnixListener::bind("/tmp/sock")?; | |
179 | /// let addr = socket.local_addr().expect("Couldn't get local address"); | |
180 | /// assert_eq!(addr.is_unnamed(), false); | |
181 | /// Ok(()) | |
182 | /// } | |
183 | /// ``` | |
184 | /// | |
185 | /// An unnamed address: | |
186 | /// | |
187 | /// ``` | |
188 | /// use std::os::unix::net::UnixDatagram; | |
189 | /// | |
190 | /// fn main() -> std::io::Result<()> { | |
191 | /// let socket = UnixDatagram::unbound()?; | |
192 | /// let addr = socket.local_addr().expect("Couldn't get local address"); | |
193 | /// assert_eq!(addr.is_unnamed(), true); | |
194 | /// Ok(()) | |
195 | /// } | |
196 | /// ``` | |
c295e0f8 | 197 | #[must_use] |
fc512014 XL |
198 | #[stable(feature = "unix_socket", since = "1.10.0")] |
199 | pub fn is_unnamed(&self) -> bool { | |
3c0e092e | 200 | matches!(self.address(), AddressKind::Unnamed) |
fc512014 XL |
201 | } |
202 | ||
203 | /// Returns the contents of this address if it is a `pathname` address. | |
204 | /// | |
205 | /// # Examples | |
206 | /// | |
207 | /// With a pathname: | |
208 | /// | |
209 | /// ```no_run | |
210 | /// use std::os::unix::net::UnixListener; | |
211 | /// use std::path::Path; | |
212 | /// | |
213 | /// fn main() -> std::io::Result<()> { | |
214 | /// let socket = UnixListener::bind("/tmp/sock")?; | |
215 | /// let addr = socket.local_addr().expect("Couldn't get local address"); | |
216 | /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); | |
217 | /// Ok(()) | |
218 | /// } | |
219 | /// ``` | |
220 | /// | |
221 | /// Without a pathname: | |
222 | /// | |
223 | /// ``` | |
224 | /// use std::os::unix::net::UnixDatagram; | |
225 | /// | |
226 | /// fn main() -> std::io::Result<()> { | |
227 | /// let socket = UnixDatagram::unbound()?; | |
228 | /// let addr = socket.local_addr().expect("Couldn't get local address"); | |
229 | /// assert_eq!(addr.as_pathname(), None); | |
230 | /// Ok(()) | |
231 | /// } | |
232 | /// ``` | |
233 | #[stable(feature = "unix_socket", since = "1.10.0")] | |
c295e0f8 | 234 | #[must_use] |
fc512014 XL |
235 | pub fn as_pathname(&self) -> Option<&Path> { |
236 | if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } | |
237 | } | |
238 | ||
c295e0f8 XL |
239 | /// Returns the contents of this address if it is an abstract namespace |
240 | /// without the leading null byte. | |
241 | /// | |
242 | /// # Examples | |
243 | /// | |
244 | /// ```no_run | |
245 | /// #![feature(unix_socket_abstract)] | |
246 | /// use std::os::unix::net::{UnixListener, SocketAddr}; | |
247 | /// | |
248 | /// fn main() -> std::io::Result<()> { | |
249 | /// let namespace = b"hidden"; | |
250 | /// let namespace_addr = SocketAddr::from_abstract_namespace(&namespace[..])?; | |
251 | /// let socket = UnixListener::bind_addr(&namespace_addr)?; | |
252 | /// let local_addr = socket.local_addr().expect("Couldn't get local address"); | |
253 | /// assert_eq!(local_addr.as_abstract_namespace(), Some(&namespace[..])); | |
254 | /// Ok(()) | |
255 | /// } | |
256 | /// ``` | |
257 | #[doc(cfg(any(target_os = "android", target_os = "linux")))] | |
258 | #[cfg(any(doc, target_os = "android", target_os = "linux",))] | |
259 | #[unstable(feature = "unix_socket_abstract", issue = "85410")] | |
260 | pub fn as_abstract_namespace(&self) -> Option<&[u8]> { | |
261 | if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } | |
262 | } | |
263 | ||
fc512014 XL |
264 | fn address(&self) -> AddressKind<'_> { |
265 | let len = self.len as usize - sun_path_offset(&self.addr); | |
266 | let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; | |
267 | ||
268 | // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses | |
269 | if len == 0 | |
270 | || (cfg!(not(any(target_os = "linux", target_os = "android"))) | |
271 | && self.addr.sun_path[0] == 0) | |
272 | { | |
273 | AddressKind::Unnamed | |
274 | } else if self.addr.sun_path[0] == 0 { | |
275 | AddressKind::Abstract(&path[1..len]) | |
276 | } else { | |
277 | AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) | |
278 | } | |
279 | } | |
c295e0f8 XL |
280 | |
281 | /// Creates an abstract domain socket address from a namespace | |
282 | /// | |
283 | /// An abstract address does not create a file unlike traditional path-based | |
284 | /// Unix sockets. The advantage of this is that the address will disappear when | |
285 | /// the socket bound to it is closed, so no filesystem clean up is required. | |
286 | /// | |
287 | /// The leading null byte for the abstract namespace is automatically added. | |
288 | /// | |
289 | /// This is a Linux-specific extension. See more at [`unix(7)`]. | |
290 | /// | |
291 | /// [`unix(7)`]: https://man7.org/linux/man-pages/man7/unix.7.html | |
292 | /// | |
293 | /// # Errors | |
294 | /// | |
295 | /// This will return an error if the given namespace is too long | |
296 | /// | |
297 | /// # Examples | |
298 | /// | |
299 | /// ```no_run | |
300 | /// #![feature(unix_socket_abstract)] | |
301 | /// use std::os::unix::net::{UnixListener, SocketAddr}; | |
302 | /// | |
303 | /// fn main() -> std::io::Result<()> { | |
304 | /// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; | |
305 | /// let listener = match UnixListener::bind_addr(&addr) { | |
306 | /// Ok(sock) => sock, | |
307 | /// Err(err) => { | |
ee023bcb | 308 | /// println!("Couldn't bind: {err:?}"); |
c295e0f8 XL |
309 | /// return Err(err); |
310 | /// } | |
311 | /// }; | |
312 | /// Ok(()) | |
313 | /// } | |
314 | /// ``` | |
315 | #[doc(cfg(any(target_os = "android", target_os = "linux")))] | |
316 | #[cfg(any(doc, target_os = "android", target_os = "linux",))] | |
317 | #[unstable(feature = "unix_socket_abstract", issue = "85410")] | |
318 | pub fn from_abstract_namespace(namespace: &[u8]) -> io::Result<SocketAddr> { | |
319 | unsafe { | |
320 | let mut addr: libc::sockaddr_un = mem::zeroed(); | |
321 | addr.sun_family = libc::AF_UNIX as libc::sa_family_t; | |
322 | ||
323 | if namespace.len() + 1 > addr.sun_path.len() { | |
5099ac24 | 324 | return Err(io::const_io_error!( |
c295e0f8 | 325 | io::ErrorKind::InvalidInput, |
5099ac24 | 326 | "namespace must be shorter than SUN_LEN", |
c295e0f8 XL |
327 | )); |
328 | } | |
329 | ||
330 | crate::ptr::copy_nonoverlapping( | |
331 | namespace.as_ptr(), | |
332 | addr.sun_path.as_mut_ptr().offset(1) as *mut u8, | |
333 | namespace.len(), | |
334 | ); | |
335 | let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t; | |
336 | SocketAddr::from_parts(addr, len) | |
337 | } | |
338 | } | |
fc512014 XL |
339 | } |
340 | ||
341 | #[stable(feature = "unix_socket", since = "1.10.0")] | |
342 | impl fmt::Debug for SocketAddr { | |
343 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | |
344 | match self.address() { | |
345 | AddressKind::Unnamed => write!(fmt, "(unnamed)"), | |
346 | AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)), | |
ee023bcb | 347 | AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"), |
fc512014 XL |
348 | } |
349 | } | |
350 | } |