use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Weak};
use anyhow::Context;
use tokio::task::JoinSet;
use tor_proto::RelayChannelAuthMaterial;
use tracing::debug;
#[cfg(unix)]
use tracing::warn;
use fs_mistrust::Mistrust;
use tor_basic_utils::iter_join;
use tor_chanmgr::{ChanMgr, ChanMgrConfig, Dormancy};
use tor_config_path::CfgPathResolver;
use tor_dirmgr::DirMgrConfig;
use tor_keymgr::{ArtiNativeKeystore, KeyMgr, KeyMgrBuilder};
use tor_memquota::MemoryQuotaTracker;
use tor_netdir::params::NetParameters;
use tor_persist::state_dir::StateDirectory;
use tor_persist::{FsStateMgr, StateMgr};
use tor_proto::relay::CreateRequestHandler;
use tor_rtcompat::{NetStreamProvider, Runtime};
use crate::client::RelayClient;
use crate::config::TorRelayConfig;
use crate::tasks::channel::build_circ_net_params;
use crate::tasks::crypto::get_ntor_keys;
#[derive(Clone)]
pub(crate) struct InertTorRelay {
config: TorRelayConfig,
dirmgr_config: DirMgrConfig,
#[expect(unused)] path_resolver: CfgPathResolver,
#[expect(unused)] state_path: PathBuf,
#[expect(unused)] state_dir: StateDirectory,
state_mgr: FsStateMgr,
keymgr: Arc<KeyMgr>,
}
impl InertTorRelay {
pub(crate) fn new(
config: TorRelayConfig,
path_resolver: CfgPathResolver,
) -> anyhow::Result<Self> {
let state_path = config.storage.state_dir(&path_resolver)?;
let cache_path = config.storage.cache_dir(&path_resolver)?;
let state_dir = StateDirectory::new(&state_path, config.storage.permissions())
.context("Failed to create `StateDirectory`")?;
let state_mgr =
FsStateMgr::from_path_and_mistrust(&state_path, config.storage.permissions())
.context("Failed to create `FsStateMgr`")?;
let _ignore_status = state_mgr
.try_lock()
.context("Failed to try locking the state manager")?;
let keymgr = Self::create_keymgr(&state_path, config.storage.permissions())
.context("Failed to create key manager")?;
let dirmgr_config = DirMgrConfig {
cache_dir: cache_path,
cache_trust: config.storage.permissions().clone(),
network: config.tor_network.clone(),
schedule: Default::default(),
tolerance: Default::default(),
override_net_params: Default::default(),
extensions: Default::default(),
};
Ok(Self {
config,
dirmgr_config,
path_resolver,
state_path,
state_dir,
state_mgr,
keymgr,
})
}
pub(crate) async fn init<R: Runtime>(self, runtime: R) -> anyhow::Result<TorRelay<R>> {
let auth_material = crate::tasks::crypto::try_generate_keys(&runtime, &self.keymgr)
.context("Failed to generate keys")?;
TorRelay::init(runtime, self, auth_material).await
}
fn create_keymgr(state_path: &Path, mistrust: &Mistrust) -> anyhow::Result<Arc<KeyMgr>> {
let key_store_dir = state_path.join("keystore");
let persistent_store = ArtiNativeKeystore::from_path_and_mistrust(&key_store_dir, mistrust)
.context("Failed to construct the native keystore")?;
debug!("Using relay keystore from {key_store_dir:?}");
let keymgr = KeyMgrBuilder::default()
.primary_store(Box::new(persistent_store))
.build()
.context("Failed to build the 'KeyMgr'")?;
let keymgr = Arc::new(keymgr);
Ok(keymgr)
}
}
pub(crate) struct TorRelay<R: Runtime> {
runtime: R,
#[expect(unused)] memquota: Arc<MemoryQuotaTracker>,
client: RelayClient<R>,
chanmgr: Arc<ChanMgr<R>>,
create_request_handler: Arc<CreateRequestHandler>,
keymgr: Arc<KeyMgr>,
or_listeners: Vec<<R as NetStreamProvider<SocketAddr>>::Listener>,
}
impl<R: Runtime> TorRelay<R> {
async fn init(
runtime: R,
inert: InertTorRelay,
auth_material: RelayChannelAuthMaterial,
) -> anyhow::Result<Self> {
let memquota = MemoryQuotaTracker::new(&runtime, inert.config.system.memory.clone())
.context("Failed to initialize memquota tracker")?;
let config = ChanMgrConfig::new(inert.config.channel.clone())
.with_my_addrs(inert.config.relay.advertise.all_addr())
.with_auth_material(Arc::new(auth_material));
let chanmgr = Arc::new(
ChanMgr::new(
runtime.clone(),
config,
Dormancy::Active,
&NetParameters::default(),
memquota.clone(),
)
.context("Failed to build chan manager")?,
);
let client = RelayClient::new(
runtime.clone(),
Arc::clone(&chanmgr),
&inert.config,
&inert.config,
inert.dirmgr_config,
inert.state_mgr,
)
.context("Failed to construct the relay's client")?;
let circ_net_params = build_circ_net_params(client.dirmgr().params().as_ref().as_ref())
.context("Failed to build circuit parameters for CREATE* request handler")?;
let ntor_keys = get_ntor_keys(&inert.keymgr)
.context("Failed to get ntor keys for CREATE* request handler")?;
let create_request_handler = CreateRequestHandler::new(
Arc::downgrade(&chanmgr) as Weak<_>,
circ_net_params,
ntor_keys,
);
let create_request_handler = Arc::new(create_request_handler);
chanmgr
.set_create_request_handler(Arc::clone(&create_request_handler))
.context("Failed to set the CREATE* request handler")?;
let or_listeners = inert.config.relay.listen.addrs().map(async |addr| {
match runtime.listen(addr).await {
Ok(x) => Some(Ok(x)),
#[cfg(unix)]
Err(ref e) if e.raw_os_error() == Some(libc::EAFNOSUPPORT) => {
let message =
format!("Could not listen at {addr}: address family not supported");
if addr.is_ipv6() {
warn!("{message}");
} else {
tor_error::warn_report!(e, "{message}");
}
None
}
Err(e) => {
Some(Err(e).with_context(|| format!("Failed to listen at address {addr}")))
}
}
});
let or_listeners = {
let mut awaited_listeners = vec![];
for listener in or_listeners {
match listener.await {
Some(Ok(x)) => awaited_listeners.push(x),
Some(Err(e)) => return Err(e),
None => {}
};
}
awaited_listeners
};
if or_listeners.is_empty() {
return Err(anyhow::anyhow!(
"Could not listen at any OR port addresses: {}",
iter_join(", ", inert.config.relay.listen.addrs()),
));
}
Ok(Self {
runtime,
memquota,
client,
chanmgr,
create_request_handler,
keymgr: inert.keymgr,
or_listeners,
})
}
pub(crate) async fn run(self) -> anyhow::Result<void::Void> {
let mut task_handles = JoinSet::new();
task_handles.spawn({
let mut t = crate::tasks::ChannelHouseKeepingTask::new(&self.chanmgr);
async move {
t.start()
.await
.context("Failed to run channel house keeping task")
}
});
task_handles.spawn({
let create_request_handler = Arc::clone(&self.create_request_handler);
let dir_provider = Arc::clone(self.client.dirmgr());
async {
crate::tasks::channel::update_create_request_handler_netparams(
create_request_handler,
dir_provider as Arc<_>,
)
.await
.context("Failed to run create request handler update task")
}
});
task_handles.spawn({
let runtime = self.runtime.clone();
let chanmgr = Arc::clone(&self.chanmgr);
async {
crate::tasks::listeners::or_listener(runtime, chanmgr, self.or_listeners)
.await
.context("Failed to run OR listener task")
}
});
task_handles.spawn({
let runtime = self.runtime.clone();
let keymgr = self.keymgr.clone();
let chanmgr = self.chanmgr.clone();
let create_request_handler = Arc::clone(&self.create_request_handler);
async {
crate::tasks::crypto::rotate_keys_task(
runtime,
keymgr,
chanmgr,
create_request_handler,
)
.await
.context("Failed to run key rotation task")
}
});
let _client_task_handles = self.client.launch_background_tasks();
self.client
.bootstrap()
.await
.context("Failed to bootstrap the relay's client")?;
let void = task_handles
.join_next()
.await
.context("Relay task set is empty")?
.context("Relay task join failed")?
.context("Relay task stopped unexpectedly")?;
void::unreachable(void);
}
pub(crate) fn keymgr(&self) -> &Arc<KeyMgr> {
&self.keymgr
}
}