use std::{io, path::PathBuf, sync::Arc};
use crate::{
ConnectError, ResolvedConnectPoint,
auth::{RpcAuth, RpcCookieSource, cookie::Cookie},
};
use fs_mistrust::Mistrust;
use tor_general_addr::general;
use tor_rtcompat::NetStreamProvider;
#[non_exhaustive]
pub struct Listener {
pub listener: tor_rtcompat::general::Listener,
pub auth: RpcAuth,
pub guard: ListenerGuard,
}
pub struct ListenerGuard {
#[allow(unused)]
rm_guard: Option<UnlinkOnDrop>,
#[allow(unused)]
lock_guard: Option<fslock_guard::LockFileGuard>,
}
struct UnlinkOnDrop(PathBuf);
impl Drop for UnlinkOnDrop {
fn drop(&mut self) {
let _ignore = std::fs::remove_file(&self.0);
}
}
impl ResolvedConnectPoint {
pub async fn bind<R>(&self, runtime: &R, mistrust: &Mistrust) -> Result<Listener, ConnectError>
where
R: NetStreamProvider<general::SocketAddr, Listener = tor_rtcompat::general::Listener>,
{
use crate::connpt::ConnectPointEnum as CptE;
match &self.0 {
CptE::Connect(connect) => connect.bind(runtime, mistrust).await,
CptE::Builtin(builtin) => builtin.bind(),
}
}
}
impl crate::connpt::Builtin {
fn bind(&self) -> Result<Listener, ConnectError> {
use crate::connpt::BuiltinVariant as BV;
match self.builtin {
BV::Abort => Err(ConnectError::ExplicitAbort),
}
}
}
impl crate::connpt::Connect<crate::connpt::Resolved> {
async fn bind<R>(&self, runtime: &R, mistrust: &Mistrust) -> Result<Listener, ConnectError>
where
R: NetStreamProvider<general::SocketAddr, Listener = tor_rtcompat::general::Listener>,
{
if let Some(sock_parent_dir) = crate::socket_parent_path(self.socket.as_ref()) {
mistrust.make_directory(sock_parent_dir)?;
}
let guard = if let Some(socket_path) = self.socket.as_ref().as_pathname() {
let lock_path = {
let mut p = socket_path.to_owned();
p.as_mut_os_string().push(".lock");
p
};
let lock_guard = Some(
fslock_guard::LockFileGuard::try_lock(lock_path)?
.ok_or(ConnectError::AlreadyLocked)?,
);
match std::fs::remove_file(socket_path) {
Ok(()) => {}
Err(e) if e.kind() == io::ErrorKind::NotFound => {}
Err(other) => return Err(other.into()),
}
let rm_guard = Some(UnlinkOnDrop(socket_path.to_owned()));
ListenerGuard {
rm_guard,
lock_guard,
}
} else {
ListenerGuard {
rm_guard: None,
lock_guard: None,
}
};
let listener = runtime.listen(self.socket.as_ref()).await?;
let auth = match &self.auth {
crate::connpt::Auth::None => RpcAuth::Inherent,
crate::connpt::Auth::Cookie { path } => RpcAuth::Cookie {
secret: RpcCookieSource::Loaded(Arc::new(Cookie::create(
path.as_path(),
&mut rand::rng(),
mistrust,
)?)),
server_address: self.socket.as_str().to_owned(),
},
crate::connpt::Auth::Unrecognized(_) => return Err(ConnectError::UnsupportedAuthType),
};
Ok(Listener {
listener,
auth,
guard,
})
}
}