interprocess/os/unix/
uds_local_socket.rs1mod listener;
4mod stream;
5
6pub use {listener::*, stream::*};
7
8#[cfg(feature = "tokio")]
10pub mod tokio {
11 mod listener;
12 mod stream;
13 pub use {listener::*, stream::*};
14}
15
16#[cfg(target_os = "android")]
17use std::os::android::net::SocketAddrExt;
18#[cfg(target_os = "linux")]
19use std::os::linux::net::SocketAddrExt;
20use {
21 crate::{
22 local_socket::{Name, NameInner},
23 os::unix::unixprelude::*,
24 },
25 std::{
26 borrow::Cow,
27 ffi::{OsStr, OsString},
28 fs, io, mem,
29 os::unix::net::SocketAddr,
30 path::Path,
31 },
32};
33
34#[derive(Clone, Debug, Default)]
35struct ReclaimGuard(Option<Name<'static>>);
36impl ReclaimGuard {
37 fn new(name: Name<'static>) -> Self { Self(if name.is_path() { Some(name) } else { None }) }
38 #[cfg_attr(not(feature = "tokio"), allow(dead_code))]
39 fn take(&mut self) -> Self { Self(self.0.take()) }
40 fn forget(&mut self) { self.0 = None; }
41}
42impl Drop for ReclaimGuard {
43 fn drop(&mut self) {
44 if let Self(Some(Name(NameInner::UdSocketPath(path)))) = self {
45 let _ = std::fs::remove_file(path);
46 }
47 }
48}
49
50#[allow(clippy::indexing_slicing)]
51fn name_to_addr(name: Name<'_>, create_dirs: bool) -> io::Result<SocketAddr> {
52 match name.0 {
53 NameInner::UdSocketPath(path) => SocketAddr::from_pathname(path),
54 NameInner::UdSocketPseudoNs(name) => construct_and_prepare_pseudo_ns(name, create_dirs),
55 #[cfg(any(target_os = "linux", target_os = "android"))]
56 NameInner::UdSocketNs(name) => SocketAddr::from_abstract_name(name),
57 }
58}
59
60const SUN_LEN: usize = {
61 let dummy = unsafe { mem::zeroed::<libc::sockaddr_un>() };
62 dummy.sun_path.len()
63};
64const NMCAP: usize = SUN_LEN - "/run/user/18446744073709551614/".len();
65
66static TOOLONG: &str = "local socket name length exceeds capacity of sun_path of sockaddr_un";
67
68fn get_run_user() -> io::Result<Option<OsString>> {
70 let path = format!("/run/user/{}", unsafe { libc::getuid() }).into();
71 match fs::metadata(&path) {
72 Ok(..) => Ok(Some(path)),
73 Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),
74 Err(e) => Err(e),
75 }
76}
77
78static TMPDIR: &str = {
79 #[cfg(target_os = "android")]
80 {
81 "/data/local/tmp"
82 }
83 #[cfg(not(target_os = "android"))]
84 {
85 "/tmp"
86 }
87};
88
89#[allow(clippy::indexing_slicing, clippy::arithmetic_side_effects)]
90fn construct_and_prepare_pseudo_ns(
91 name: Cow<'_, OsStr>,
92 create_dirs: bool,
93) -> io::Result<SocketAddr> {
94 let nlen = name.len();
95 if nlen > NMCAP {
96 return Err(io::Error::new(io::ErrorKind::InvalidInput, TOOLONG));
97 }
98 let run_user = get_run_user()?;
99 let pfx = run_user.map(Cow::Owned).unwrap_or(Cow::Borrowed(OsStr::new(TMPDIR)));
100 let pl = pfx.len();
101 let mut path = [0; SUN_LEN];
102 path[..pl].copy_from_slice(pfx.as_bytes());
103 path[pl] = b'/';
104
105 let namestart = pl + 1;
106 let fulllen = pl + 1 + nlen;
107 path[namestart..fulllen].copy_from_slice(name.as_bytes());
108
109 const ESCCHAR: u8 = b'_';
110 for byte in path[namestart..fulllen].iter_mut() {
111 if *byte == 0 {
112 *byte = ESCCHAR;
113 }
114 }
115
116 let opath = Path::new(OsStr::from_bytes(&path[..fulllen]));
117
118 if create_dirs {
119 let parent = opath.parent();
120 if let Some(p) = parent {
121 fs::create_dir_all(p)?;
122 }
123 }
124 SocketAddr::from_pathname(opath)
125}