parsec_service/front/
domain_socket.rs1use super::listener;
8use anyhow::{Context, Result};
9use listener::Listen;
10use listener::{Connection, ConnectionMetadata};
11use log::{error, warn};
12use std::fs;
13use std::fs::Permissions;
14use std::io::{Error, ErrorKind};
15use std::os::unix::fs::FileTypeExt;
16use std::os::unix::fs::PermissionsExt;
17use std::os::unix::io::FromRawFd;
18use std::os::unix::io::RawFd;
19use std::os::unix::net::UnixListener;
20use std::path::PathBuf;
21use std::time::Duration;
22
23static DEFAULT_SOCKET_PATH: &str = "/run/parsec/parsec.sock";
24
25#[derive(Debug)]
31pub struct DomainSocketListener {
32 listener: UnixListener,
33 timeout: Duration,
34}
35
36impl DomainSocketListener {
37 pub fn new(timeout: Duration, socket_path: PathBuf) -> Result<Self> {
39 let listeners: Vec<RawFd> = sd_notify::listen_fds()?.collect();
42 let listener = match listeners.len() {
43 0 => {
44 if socket_path.exists() {
45 let meta = fs::metadata(&socket_path)?;
46 if meta.file_type().is_socket() {
47 warn!(
48 "Removing the existing socket file at {}.",
49 socket_path.display()
50 );
51 fs::remove_file(&socket_path)?;
52 } else {
53 error!(
54 "A file exists at {} but is not a Unix Domain Socket.",
55 socket_path.display()
56 );
57 }
58 }
59
60 let listener = UnixListener::bind(&socket_path).with_context(|| {
62 format!("Failed to bind to Unix socket at {:?}", socket_path)
63 })?;
64 listener.set_nonblocking(true)?;
65
66 let permissions = Permissions::from_mode(0o666);
69 fs::set_permissions(socket_path, permissions)?;
70
71 listener
72 }
73 1 => {
74 let nfd = listeners[0];
77 unsafe { UnixListener::from_raw_fd(nfd) }
80 }
82 n => {
83 error!(
84 "Received too many file descriptors ({} received, 0 or 1 expected).",
85 n
86 );
87 return Err(Error::new(
88 ErrorKind::InvalidData,
89 "too many file descriptors received",
90 )
91 .into());
92 }
93 };
94
95 Ok(Self { listener, timeout })
96 }
97}
98
99impl Listen for DomainSocketListener {
100 fn set_timeout(&mut self, duration: Duration) {
101 self.timeout = duration;
102 }
103
104 fn accept(&self) -> Option<Connection> {
105 let stream_result = self.listener.accept();
106 match stream_result {
107 Ok((stream, _)) => {
108 if let Err(err) = stream.set_read_timeout(Some(self.timeout)) {
109 format_error!("Failed to set read timeout", err);
110 None
111 } else if let Err(err) = stream.set_write_timeout(Some(self.timeout)) {
112 format_error!("Failed to set write timeout", err);
113 None
114 } else if let Err(err) = stream.set_nonblocking(false) {
115 format_error!("Failed to set stream as blocking", err);
116 None
117 } else {
118 let ucred = peer_credentials::peer_cred(&stream)
119 .map_err(|err| {
120 format_error!(
121 "Failed to grab peer credentials metadata from UnixStream",
122 err
123 );
124 err
125 })
126 .ok()?;
127 Some(Connection {
128 stream: Box::new(stream),
129 metadata: Some(ConnectionMetadata::UnixPeerCredentials {
130 uid: ucred.uid,
131 gid: ucred.gid,
132 pid: ucred.pid,
133 }),
134 })
135 }
136 }
137 Err(err) => {
138 if err.kind() != ErrorKind::WouldBlock {
140 format_error!("Failed to connect with a UnixStream", err);
142 }
143 None
144 }
145 }
146 }
147}
148
149#[derive(Clone, Debug, Default)]
151pub struct DomainSocketListenerBuilder {
152 timeout: Option<Duration>,
153 socket_path: Option<PathBuf>,
154}
155
156impl DomainSocketListenerBuilder {
157 pub fn new() -> Self {
159 DomainSocketListenerBuilder {
160 timeout: None,
161 socket_path: None,
162 }
163 }
164
165 pub fn with_timeout(mut self, timeout: Duration) -> Self {
167 self.timeout = Some(timeout);
168 self
169 }
170
171 pub fn with_socket_path(mut self, socket_path: Option<PathBuf>) -> Self {
173 self.socket_path = socket_path;
174 self
175 }
176
177 pub fn build(self) -> Result<DomainSocketListener> {
179 DomainSocketListener::new(
180 self.timeout.ok_or_else(|| {
181 error!("The listener timeout was not set.");
182 Error::new(ErrorKind::InvalidInput, "listener timeout missing")
183 })?,
184 self.socket_path
185 .unwrap_or_else(|| DEFAULT_SOCKET_PATH.into()),
186 )
187 }
188}
189
190pub mod peer_credentials {
207 use libc::{gid_t, pid_t, uid_t};
208
209 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
211 pub struct UCred {
212 pub uid: uid_t,
215 pub gid: gid_t,
218 pub pid: Option<pid_t>,
223 }
224
225 #[cfg(any(target_os = "android", target_os = "linux"))]
226 pub use self::impl_linux::peer_cred;
227
228 #[cfg(any(
229 target_os = "dragonfly",
230 target_os = "freebsd",
231 target_os = "ios",
232 target_os = "macos",
233 target_os = "openbsd"
234 ))]
235 pub use self::impl_bsd::peer_cred;
236
237 #[cfg(any(target_os = "linux", target_os = "android"))]
238 #[allow(missing_docs, trivial_casts)] pub mod impl_linux {
240 use super::UCred;
241 use libc::{c_void, getsockopt, socklen_t, ucred, SOL_SOCKET, SO_PEERCRED};
242 use std::os::unix::io::AsRawFd;
243 use std::os::unix::net::UnixStream;
244 use std::{io, mem};
245
246 pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
247 let ucred_size = mem::size_of::<ucred>();
248
249 assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
251 assert!(ucred_size <= u32::MAX as usize);
252
253 let mut ucred_size = ucred_size as socklen_t;
254 let mut ucred: ucred = ucred {
255 pid: 1,
256 uid: 1,
257 gid: 1,
258 };
259
260 unsafe {
261 let ret = getsockopt(
262 socket.as_raw_fd(),
263 SOL_SOCKET,
264 SO_PEERCRED,
265 &mut ucred as *mut ucred as *mut c_void,
266 &mut ucred_size,
267 );
268
269 if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
270 Ok(UCred {
271 uid: ucred.uid,
272 gid: ucred.gid,
273 pid: Some(ucred.pid),
274 })
275 } else {
276 Err(io::Error::last_os_error())
277 }
278 }
279 }
280 }
281
282 #[cfg(any(
283 target_os = "dragonfly",
284 target_os = "macos",
285 target_os = "ios",
286 target_os = "freebsd",
287 target_os = "openbsd"
288 ))]
289 #[allow(missing_docs)] pub mod impl_bsd {
291 use super::UCred;
292 use std::io;
293 use std::os::unix::io::AsRawFd;
294 use std::os::unix::net::UnixStream;
295
296 pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
297 let mut cred = UCred {
298 uid: 1,
299 gid: 1,
300 pid: None,
301 };
302 unsafe {
303 let ret = libc::getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid);
304
305 if ret == 0 {
306 Ok(cred)
307 } else {
308 Err(io::Error::last_os_error())
309 }
310 }
311 }
312 }
313}