1use crate::CoreError;
18use crate::error::syscall_ret;
19use crate::fd::Fd;
20use std::io::Error as IoError;
21use std::os::unix::ffi::OsStrExt;
22use std::os::unix::fs::FileTypeExt;
23use std::os::unix::io::AsRawFd;
24use std::path::Path;
25
26#[inline(always)]
27fn errno() -> i32 {
28 IoError::last_os_error().raw_os_error().unwrap_or(0)
29}
30
31pub struct UnixListener {
33 fd: Fd,
35}
36
37pub struct UnixStream {
39 fd: Fd,
41}
42
43impl UnixListener {
44 pub fn fd(&self) -> &Fd {
47 &self.fd
48 }
49}
50
51impl UnixStream {
52 pub fn fd(&self) -> &Fd {
55 &self.fd
56 }
57}
58
59pub enum UnixConnectResult {
61 Connected(UnixStream),
63 InProgress(UnixStream),
66}
67
68#[derive(Clone, Copy, Debug)]
70pub enum UnixSocketAddr<'a> {
71 Path(&'a Path),
73 Abstract(&'a [u8]),
75}
76
77#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
79pub enum StaleSocketPolicy {
80 #[default]
82 Preserve,
83 UnlinkSocketOnly,
85 UnlinkAnyPath,
90}
91
92#[derive(Clone, Copy, Debug, Default)]
94pub struct UnixSocketBindOptions {
95 pub stale_socket_policy: StaleSocketPolicy,
97 pub mode: Option<u32>,
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub struct PeerCred {
104 pub pid: Option<i32>,
106 pub uid: u32,
108 pub gid: u32,
110}
111
112impl UnixListener {
113 pub fn accept(&self) -> Result<Option<UnixStream>, CoreError> {
127 self.accept_timeout(0)
128 }
129
130 pub fn accept_timeout(&self, timeout_ms: i32) -> Result<Option<UnixStream>, CoreError> {
146 if timeout_ms != 0 {
147 let mut pollfd = libc::pollfd {
148 fd: self.fd.as_raw_fd(),
149 events: libc::POLLIN,
150 revents: 0,
151 };
152 let ret = unsafe { libc::poll(&mut pollfd, 1, timeout_ms) };
153 if ret < 0 {
154 let e = errno();
155 if e == libc::EINTR {
156 return Ok(None);
157 }
158 return Err(CoreError::sys(e, "poll(accept)"));
159 }
160 if ret == 0 {
161 return Ok(None);
162 }
163 }
164
165 loop {
166 let fd = unsafe {
167 libc::accept4(
168 self.fd.as_raw_fd(),
169 std::ptr::null_mut(),
170 std::ptr::null_mut(),
171 libc::SOCK_CLOEXEC | libc::SOCK_NONBLOCK,
172 )
173 };
174 if fd >= 0 {
175 return Ok(Some(UnixStream {
176 fd: Fd::new(fd, "accept4")?,
177 }));
178 }
179
180 let e = errno();
181 if e == libc::EINTR {
182 continue;
183 }
184 if e == libc::EAGAIN || e == libc::EWOULDBLOCK {
185 return Ok(None);
186 }
187 return Err(CoreError::sys(e, "accept4"));
188 }
189 }
190}
191
192impl UnixStream {
193 pub fn peer_cred(&self) -> Result<Option<PeerCred>, CoreError> {
210 peer_cred_raw(&self.fd)
211 }
212
213 pub fn check_connect_error(&self) -> Result<Option<i32>, CoreError> {
221 let mut code: libc::c_int = 0;
222 let mut len = std::mem::size_of::<libc::c_int>() as libc::socklen_t;
223 let ret = unsafe {
224 libc::getsockopt(
225 self.fd.as_raw_fd(),
226 libc::SOL_SOCKET,
227 libc::SO_ERROR,
228 (&mut code as *mut libc::c_int).cast(),
229 &mut len,
230 )
231 };
232 syscall_ret(ret, "getsockopt(SO_ERROR)")?;
233 if code == 0 { Ok(None) } else { Ok(Some(code)) }
234 }
235
236 pub fn finish_connect(self) -> Result<Self, CoreError> {
245 match self.check_connect_error()? {
246 None => Ok(self),
247 Some(code) => Err(CoreError::sys(code, "connect(SO_ERROR)")),
248 }
249 }
250}
251
252pub fn bind(
267 addr: UnixSocketAddr<'_>,
268 opts: UnixSocketBindOptions,
269) -> Result<UnixListener, CoreError> {
270 let encoded = UnixSockAddr::new(addr, "unix bind address")?;
271
272 match addr {
273 UnixSocketAddr::Path(path) => {
274 apply_stale_socket_policy(path, opts.stale_socket_policy)?;
275 }
276 UnixSocketAddr::Abstract(_) => {
277 if opts.stale_socket_policy != StaleSocketPolicy::Preserve || opts.mode.is_some() {
278 return Err(CoreError::sys(libc::EINVAL, "abstract unix bind options"));
279 }
280 }
281 }
282
283 let fd = new_unix_stream_socket()?;
284 let ret = unsafe { libc::bind(fd.as_raw_fd(), encoded.as_ptr(), encoded.len()) };
285 syscall_ret(ret, "bind")?;
286
287 if let (UnixSocketAddr::Path(path), Some(mode)) = (addr, opts.mode) {
288 if let Err(err) = chmod(UnixSocketAddr::Path(path), mode) {
289 cleanup_created_path(addr);
290 return Err(err);
291 }
292 }
293
294 let ret = unsafe { libc::listen(fd.as_raw_fd(), libc::SOMAXCONN) };
295 if let Err(err) = syscall_ret(ret, "listen") {
296 cleanup_created_path(addr);
297 return Err(err);
298 }
299
300 Ok(UnixListener { fd })
301}
302
303pub fn connect(addr: UnixSocketAddr<'_>) -> Result<UnixConnectResult, CoreError> {
314 connect_as(addr, None)
315}
316
317pub fn connect_named(
320 remote: UnixSocketAddr<'_>,
321 local: UnixSocketAddr<'_>,
322) -> Result<UnixConnectResult, CoreError> {
323 connect_as(remote, Some(local))
324}
325
326fn connect_as(
327 remote: UnixSocketAddr<'_>,
328 local: Option<UnixSocketAddr<'_>>,
329) -> Result<UnixConnectResult, CoreError> {
330 let encoded = UnixSockAddr::new(remote, "unix connect address")?;
331 let fd = new_unix_stream_socket()?;
332
333 if let Some(la) = local {
334 let lb = UnixSockAddr::new(la, "unix bind local name")?;
335 let r = unsafe { libc::bind(fd.as_raw_fd(), lb.as_ptr(), lb.len()) };
336 if r < 0 {
337 return Err(CoreError::sys(errno(), "bind local name"));
338 }
339 }
340
341 loop {
342 let ret = unsafe { libc::connect(fd.as_raw_fd(), encoded.as_ptr(), encoded.len()) };
343 if ret == 0 {
344 return Ok(UnixConnectResult::Connected(UnixStream { fd }));
345 }
346
347 let e = errno();
348 if e == libc::EINTR {
349 continue;
350 }
351 if e == libc::EINPROGRESS || e == libc::EALREADY {
352 return Ok(UnixConnectResult::InProgress(UnixStream { fd }));
353 }
354 if e == libc::EISCONN {
355 return Ok(UnixConnectResult::Connected(UnixStream { fd }));
356 }
357 return Err(CoreError::sys(e, "connect"));
358 }
359}
360
361pub fn socketpair() -> Result<(UnixStream, UnixStream), CoreError> {
371 let mut fds = [0 as libc::c_int; 2];
372 let ret = unsafe {
373 libc::socketpair(
374 libc::AF_UNIX,
375 libc::SOCK_STREAM | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC,
376 0,
377 fds.as_mut_ptr(),
378 )
379 };
380 syscall_ret(ret, "socketpair")?;
381 let a = UnixStream {
382 fd: Fd::new(fds[0], "socketpair.0")?,
383 };
384 let b = UnixStream {
385 fd: Fd::new(fds[1], "socketpair.1")?,
386 };
387 Ok((a, b))
388}
389
390pub fn chmod(addr: UnixSocketAddr<'_>, mode: u32) -> Result<(), CoreError> {
397 match addr {
398 UnixSocketAddr::Path(path) => {
399 let metadata = std::fs::symlink_metadata(path).map_err(|err| {
400 CoreError::sys(
401 err.raw_os_error().unwrap_or(libc::EIO),
402 "lstat unix socket path",
403 )
404 })?;
405 if !metadata.file_type().is_socket() {
406 return Err(CoreError::sys(libc::EINVAL, "chmod unix socket path"));
407 }
408 let c_path = path_cstring(path, "chmod unix socket path")?;
409 let ret = unsafe { libc::chmod(c_path.as_ptr(), mode as libc::mode_t) };
410 syscall_ret(ret, "chmod")
411 }
412 UnixSocketAddr::Abstract(_) => Err(CoreError::sys(libc::EINVAL, "chmod abstract socket")),
413 }
414}
415
416pub fn chmod_path(path: impl AsRef<Path>, mode: u32) -> Result<(), CoreError> {
418 chmod(UnixSocketAddr::Path(path.as_ref()), mode)
419}
420
421fn new_unix_stream_socket() -> Result<Fd, CoreError> {
425 let fd = unsafe {
426 libc::socket(
427 libc::AF_UNIX,
428 libc::SOCK_STREAM | libc::SOCK_CLOEXEC | libc::SOCK_NONBLOCK,
429 0,
430 )
431 };
432 syscall_ret(fd, "socket(AF_UNIX)")?;
433 Fd::new(fd, "socket(AF_UNIX)")
434}
435
436fn apply_stale_socket_policy(path: &Path, policy: StaleSocketPolicy) -> Result<(), CoreError> {
437 match policy {
438 StaleSocketPolicy::Preserve => Ok(()),
439 StaleSocketPolicy::UnlinkSocketOnly => {
440 let metadata = match std::fs::symlink_metadata(path) {
441 Ok(metadata) => metadata,
442 Err(err) if err.raw_os_error() == Some(libc::ENOENT) => return Ok(()),
443 Err(err) => {
444 return Err(CoreError::sys(
445 err.raw_os_error().unwrap_or(libc::EIO),
446 "lstat unix socket path",
447 ));
448 }
449 };
450 if !metadata.file_type().is_socket() {
451 return Err(CoreError::sys(libc::EEXIST, "stale unix socket path"));
452 }
453 unlink_path(path, "unlink stale unix socket")
454 }
455 StaleSocketPolicy::UnlinkAnyPath => unlink_path(path, "unlink unix socket path"),
456 }
457}
458
459fn unlink_path(path: &Path, op: &'static str) -> Result<(), CoreError> {
460 match std::fs::remove_file(path) {
461 Ok(()) => Ok(()),
462 Err(err) if err.raw_os_error() == Some(libc::ENOENT) => Ok(()),
463 Err(err) => Err(CoreError::sys(err.raw_os_error().unwrap_or(libc::EIO), op)),
464 }
465}
466
467fn cleanup_created_path(addr: UnixSocketAddr<'_>) {
468 if let UnixSocketAddr::Path(path) = addr {
469 let _ = std::fs::remove_file(path);
470 }
471}
472
473struct UnixSockAddr {
474 inner: libc::sockaddr_un,
475 len: libc::socklen_t,
476}
477
478impl UnixSockAddr {
479 fn new(addr: UnixSocketAddr<'_>, op: &'static str) -> Result<Self, CoreError> {
480 let mut inner: libc::sockaddr_un = unsafe { std::mem::zeroed() };
481 inner.sun_family = libc::AF_UNIX as libc::sa_family_t;
482 let sun_path_offset = std::mem::offset_of!(libc::sockaddr_un, sun_path);
483
484 let len = match addr {
485 UnixSocketAddr::Path(path) => {
486 let bytes = path.as_os_str().as_bytes();
487 if bytes.is_empty() {
488 return Err(CoreError::sys(libc::EINVAL, op));
489 }
490 if bytes.contains(&0) {
491 return Err(CoreError::sys(libc::EINVAL, op));
492 }
493 if bytes.len() >= inner.sun_path.len() {
494 return Err(CoreError::sys(libc::ENAMETOOLONG, op));
495 }
496
497 for (slot, byte) in inner.sun_path.iter_mut().zip(bytes.iter().copied()) {
498 *slot = byte as libc::c_char;
499 }
500 sun_path_offset + bytes.len() + 1
501 }
502 UnixSocketAddr::Abstract(name) => {
503 validate_abstract_supported()?;
504 if name.is_empty() {
505 return Err(CoreError::sys(libc::EINVAL, op));
506 }
507 if name.len() + 1 > inner.sun_path.len() {
508 return Err(CoreError::sys(libc::ENAMETOOLONG, op));
509 }
510
511 inner.sun_path[0] = 0;
512 for (slot, byte) in inner.sun_path[1..].iter_mut().zip(name.iter().copied()) {
513 *slot = byte as libc::c_char;
514 }
515 sun_path_offset + 1 + name.len()
516 }
517 };
518 let len = libc::socklen_t::try_from(len).map_err(|_| CoreError::sys(libc::EINVAL, op))?;
519
520 Ok(Self { inner, len })
521 }
522
523 fn len(&self) -> libc::socklen_t {
524 self.len
525 }
526
527 fn as_ptr(&self) -> *const libc::sockaddr {
528 (&self.inner as *const libc::sockaddr_un).cast()
529 }
530}
531
532fn validate_abstract_supported() -> Result<(), CoreError> {
533 if cfg!(any(target_os = "linux", target_os = "android")) {
534 Ok(())
535 } else {
536 Err(CoreError::sys(libc::ENOSYS, "abstract unix socket"))
537 }
538}
539
540fn path_cstring(path: &Path, op: &'static str) -> Result<std::ffi::CString, CoreError> {
541 std::ffi::CString::new(path.as_os_str().as_bytes())
542 .map_err(|_| CoreError::sys(libc::EINVAL, op))
543}
544
545#[cfg(any(target_os = "linux", target_os = "android"))]
546fn peer_cred_raw(fd: &Fd) -> Result<Option<PeerCred>, CoreError> {
547 let mut cred: libc::ucred = unsafe { std::mem::zeroed() };
548 let mut len = std::mem::size_of::<libc::ucred>() as libc::socklen_t;
549 let ret = unsafe {
550 libc::getsockopt(
551 fd.as_raw_fd(),
552 libc::SOL_SOCKET,
553 libc::SO_PEERCRED,
554 (&mut cred as *mut libc::ucred).cast(),
555 &mut len,
556 )
557 };
558 syscall_ret(ret, "getsockopt(SO_PEERCRED)")?;
559
560 Ok(Some(PeerCred {
561 pid: Some(cred.pid),
562 uid: cred.uid,
563 gid: cred.gid,
564 }))
565}
566
567#[cfg(not(any(target_os = "linux", target_os = "android")))]
568fn peer_cred_raw(_fd: &Fd) -> Result<Option<PeerCred>, CoreError> {
569 Ok(None)
570}