use std::io;
use std::path::Path;
use tokio::net::UnixStream;
pub type LocalStream = UnixStream;
pub type LocalReadHalf = tokio::net::unix::OwnedReadHalf;
pub type LocalWriteHalf = tokio::net::unix::OwnedWriteHalf;
pub fn split_local(stream: LocalStream) -> (LocalReadHalf, LocalWriteHalf) {
stream.into_split()
}
pub async fn connect_local(path: &Path) -> io::Result<LocalStream> {
UnixStream::connect(path).await
}
#[cfg(feature = "service")]
mod server {
use super::*;
use std::os::unix::fs::PermissionsExt;
use tokio::net::UnixListener;
pub fn ensure_private_dir(dir: &Path) -> io::Result<()> {
use std::os::unix::fs::MetadataExt;
std::fs::create_dir_all(dir)?;
let is_symlink = std::fs::symlink_metadata(dir)?.file_type().is_symlink();
if is_symlink {
return Err(io::Error::other(format!(
"runtime dir {} is a symlink; refusing",
dir.display()
)));
}
std::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o700))?;
let meta = std::fs::metadata(dir)?;
if meta.uid() != rustix::process::geteuid().as_raw() {
return Err(io::Error::other(format!(
"runtime dir {} is not owned by us",
dir.display()
)));
}
Ok(())
}
pub fn bind_uds(path: &Path) -> io::Result<UnixListener> {
if let Some(parent) = path.parent()
&& !parent.as_os_str().is_empty()
{
ensure_private_dir(parent)?;
}
let _ = std::fs::remove_file(path);
let listener = UnixListener::bind(path)
.map_err(|e| io::Error::new(e.kind(), format!("bind {path:?}: {e}")))?;
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600))
.map_err(|e| io::Error::new(e.kind(), format!("chmod 0600 {path:?}: {e}")))?;
Ok(listener)
}
pub fn check_peer_uid(stream: &UnixStream) -> bool {
let Ok(cred) = stream.peer_cred() else {
tracing::warn!("peer_cred unreadable: refusing local connection");
return false;
};
let peer = cred.uid();
let me = rustix::process::geteuid().as_raw();
if peer != me {
tracing::warn!(peer, me, "refusing cross-uid local connection");
return false;
}
true
}
pub struct LocalListener(UnixListener);
impl LocalListener {
pub async fn accept(&mut self) -> io::Result<LocalStream> {
let (stream, _addr) = self.0.accept().await?;
Ok(stream)
}
}
pub fn authorize_local_peer(stream: &LocalStream) -> bool {
check_peer_uid(stream)
}
pub fn bind_local(path: &Path) -> io::Result<LocalListener> {
Ok(LocalListener(bind_uds(path)?))
}
}
#[cfg(feature = "service")]
pub use server::*;