use crate::build::TunnelBuilder;
use crate::mgr::{self, MockablePlan};
use crate::path::OwnedPath;
use crate::usage::{SupportedTunnelUsage, TargetTunnelUsage};
use crate::{DirInfo, Error, PathConfig, Result, timeouts};
use async_trait::async_trait;
use educe::Educe;
use futures::future::OptionFuture;
use std::sync::Arc;
use tor_basic_utils::skip_fmt;
use tor_error::{bad_api_usage, internal};
#[cfg(feature = "vanguards")]
use tor_guardmgr::vanguards::VanguardMgr;
use tor_linkspec::CircTarget;
use tor_proto::ClientTunnel;
use tor_proto::circuit::UniqId;
use tor_proto::client::circuit::{CircParameters, Path};
use tor_rtcompat::Runtime;
use tracing::instrument;
#[async_trait]
impl mgr::AbstractTunnel for tor_proto::ClientTunnel {
type Id = tor_proto::circuit::UniqId;
fn id(&self) -> Self::Id {
self.unique_id()
}
fn usable(&self) -> bool {
!self.is_closing()
}
#[allow(unstable_name_collisions)]
fn single_path(&self) -> tor_proto::Result<Arc<Path>> {
use itertools::Itertools as _;
self.all_paths().into_iter().exactly_one().map_err(|_| {
bad_api_usage!("requested the single path of a multi-path tunnel?!").into()
})
}
fn n_hops(&self) -> tor_proto::Result<usize> {
self.n_hops()
}
fn is_closing(&self) -> bool {
self.is_closed()
}
fn unique_id(&self) -> UniqId {
self.unique_id()
}
async fn extend<T: CircTarget + Sync>(
&self,
target: &T,
params: CircParameters,
) -> tor_proto::Result<()> {
let circ = self.as_single_circ()?;
circ.extend(target, params).await
}
async fn last_known_to_be_used_at(&self) -> tor_proto::Result<Option<std::time::Instant>> {
self.disused_since().await
}
}
#[derive(Educe)]
#[educe(Debug)]
pub(crate) struct Plan {
final_spec: SupportedTunnelUsage,
path: OwnedPath,
params: CircParameters,
guard_status: Option<tor_guardmgr::GuardMonitor>,
#[educe(Debug(method = "skip_fmt"))]
guard_usable: Option<tor_guardmgr::GuardUsable>,
}
impl MockablePlan for Plan {}
#[async_trait]
impl<R: Runtime> crate::mgr::AbstractTunnelBuilder<R> for crate::build::TunnelBuilder<R> {
type Tunnel = ClientTunnel;
type Plan = Plan;
#[instrument(level = "trace", skip_all)]
fn plan_tunnel(
&self,
usage: &TargetTunnelUsage,
dir: DirInfo<'_>,
) -> Result<(Plan, SupportedTunnelUsage)> {
let mut rng = rand::rng();
let (path, final_spec, guard_status, guard_usable) = usage.build_path(
&mut rng,
dir,
self.guardmgr(),
#[cfg(all(feature = "vanguards", feature = "hs-common"))]
self.vanguardmgr(),
self.path_config().as_ref(),
self.runtime().wallclock(),
)?;
let plan = Plan {
final_spec: final_spec.clone(),
path: (&path).try_into()?,
params: dir.circ_params(usage)?,
guard_status,
guard_usable,
};
Ok((plan, final_spec))
}
#[instrument(level = "trace", skip_all)]
async fn build_tunnel(&self, plan: Plan) -> Result<(SupportedTunnelUsage, Self::Tunnel)> {
use crate::build::GuardStatusHandle;
use tor_guardmgr::GuardStatus;
let Plan {
final_spec,
path,
params,
guard_status,
guard_usable,
} = plan;
let guard_usable: OptionFuture<_> = guard_usable.into();
let guard_status: Arc<GuardStatusHandle> = Arc::new(guard_status.into());
guard_status.pending(GuardStatus::AttemptAbandoned);
match self
.build_owned(
path,
¶ms,
Arc::clone(&guard_status),
final_spec.channel_usage(),
)
.await
{
Ok(tunnel) => {
guard_status.report(GuardStatus::Success);
match guard_usable.await {
Some(Ok(true)) | None => (),
Some(Ok(false)) => return Err(Error::GuardNotUsable(tunnel.unique_id())),
Some(Err(_)) => {
return Err(internal!("Guard usability status cancelled").into());
}
}
Ok((final_spec, tunnel))
}
Err(e) => {
guard_status.commit();
Err(e)
}
}
}
fn launch_parallelism(&self, spec: &TargetTunnelUsage) -> usize {
match spec {
TargetTunnelUsage::Dir => 3,
_ => 1,
}
}
fn select_parallelism(&self, spec: &TargetTunnelUsage) -> usize {
self.launch_parallelism(spec)
}
fn learning_timeouts(&self) -> bool {
TunnelBuilder::learning_timeouts(self)
}
fn save_state(&self) -> Result<bool> {
TunnelBuilder::save_state(self)
}
fn path_config(&self) -> Arc<PathConfig> {
TunnelBuilder::path_config(self)
}
fn set_path_config(&self, new_config: PathConfig) {
TunnelBuilder::set_path_config(self, new_config);
}
fn estimator(&self) -> &timeouts::Estimator {
TunnelBuilder::estimator(self)
}
#[cfg(feature = "vanguards")]
fn vanguardmgr(&self) -> &Arc<VanguardMgr<R>> {
TunnelBuilder::vanguardmgr(self)
}
#[instrument(level = "trace", skip_all)]
fn upgrade_to_owned_state(&self) -> Result<()> {
TunnelBuilder::upgrade_to_owned_state(self)
}
#[instrument(level = "trace", skip_all)]
fn reload_state(&self) -> Result<()> {
TunnelBuilder::reload_state(self)
}
fn guardmgr(&self) -> &tor_guardmgr::GuardMgr<R> {
TunnelBuilder::guardmgr(self)
}
fn update_network_parameters(&self, p: &tor_netdir::params::NetParameters) {
TunnelBuilder::update_network_parameters(self, p);
}
}