tor_circmgr/
impls.rs

1//! Implement traits from [`crate::mgr`] for the circuit types we use.
2
3use crate::build::CircuitBuilder;
4use crate::mgr::{self, MockablePlan};
5use crate::path::OwnedPath;
6use crate::usage::{SupportedCircUsage, TargetCircUsage};
7use crate::{timeouts, DirInfo, Error, PathConfig, Result};
8use async_trait::async_trait;
9use educe::Educe;
10use futures::future::OptionFuture;
11use std::sync::Arc;
12use tor_basic_utils::skip_fmt;
13use tor_error::internal;
14#[cfg(feature = "vanguards")]
15use tor_guardmgr::vanguards::VanguardMgr;
16use tor_linkspec::CircTarget;
17use tor_proto::circuit::{CircParameters, ClientCirc, Path, UniqId};
18use tor_rtcompat::Runtime;
19
20#[async_trait]
21impl mgr::AbstractCirc for tor_proto::circuit::ClientCirc {
22    type Id = tor_proto::circuit::UniqId;
23
24    fn id(&self) -> Self::Id {
25        self.unique_id()
26    }
27
28    fn usable(&self) -> bool {
29        !self.is_closing()
30    }
31
32    fn path_ref(&self) -> tor_proto::Result<Arc<Path>> {
33        self.path_ref()
34    }
35
36    fn n_hops(&self) -> tor_proto::Result<usize> {
37        self.n_hops()
38    }
39
40    fn is_closing(&self) -> bool {
41        self.is_closing()
42    }
43
44    fn unique_id(&self) -> UniqId {
45        self.unique_id()
46    }
47
48    async fn extend<T: CircTarget + std::marker::Sync>(
49        &self,
50        target: &T,
51        params: CircParameters,
52    ) -> tor_proto::Result<()> {
53        // Use 'ClientCirc::' name to avoid invoking _this_ method.
54        ClientCirc::extend(self, target, params).await
55    }
56}
57
58/// The information generated by circuit planning, and used to build a
59/// circuit.
60#[derive(Educe)]
61#[educe(Debug)]
62pub(crate) struct Plan {
63    /// The supported usage that the circuit will have when complete
64    final_spec: SupportedCircUsage,
65    /// An owned copy of the path to build.
66    // TODO: it would be nice if this weren't owned.
67    path: OwnedPath,
68    /// The protocol parameters to use when constructing the circuit.
69    params: CircParameters,
70    /// If this path is using a guard, we'll use this object to report
71    /// whether the circuit succeeded or failed.
72    guard_status: Option<tor_guardmgr::GuardMonitor>,
73    /// If this path is using a guard, we'll use this object to learn
74    /// whether we're allowed to use the circuit or whether we have to
75    /// wait a while.
76    #[educe(Debug(method = "skip_fmt"))]
77    guard_usable: Option<tor_guardmgr::GuardUsable>,
78}
79
80impl MockablePlan for Plan {}
81
82#[async_trait]
83impl<R: Runtime> crate::mgr::AbstractCircBuilder<R> for crate::build::CircuitBuilder<R> {
84    type Circ = ClientCirc;
85    type Plan = Plan;
86
87    fn plan_circuit(
88        &self,
89        usage: &TargetCircUsage,
90        dir: DirInfo<'_>,
91    ) -> Result<(Plan, SupportedCircUsage)> {
92        let mut rng = rand::rng();
93        let (path, final_spec, guard_status, guard_usable) = usage.build_path(
94            &mut rng,
95            dir,
96            self.guardmgr(),
97            #[cfg(all(feature = "vanguards", feature = "hs-common"))]
98            self.vanguardmgr(),
99            self.path_config().as_ref(),
100            self.runtime().wallclock(),
101        )?;
102
103        let plan = Plan {
104            final_spec: final_spec.clone(),
105            path: (&path).try_into()?,
106            params: dir.circ_params(usage)?,
107            guard_status,
108            guard_usable,
109        };
110
111        Ok((plan, final_spec))
112    }
113
114    async fn build_circuit(&self, plan: Plan) -> Result<(SupportedCircUsage, Arc<ClientCirc>)> {
115        use crate::build::GuardStatusHandle;
116        use tor_guardmgr::GuardStatus;
117        let Plan {
118            final_spec,
119            path,
120            params,
121            guard_status,
122            guard_usable,
123        } = plan;
124
125        let guard_usable: OptionFuture<_> = guard_usable.into();
126        let guard_status: Arc<GuardStatusHandle> = Arc::new(guard_status.into());
127
128        guard_status.pending(GuardStatus::AttemptAbandoned);
129
130        // TODO: We may want to lower the logic for handling
131        // guard_status and guard_usable into build.rs, so that they
132        // can be handled correctly on user-selected paths as well.
133        //
134        // This will probably require a different API for circuit
135        // construction.
136        match self
137            .build_owned(
138                path,
139                &params,
140                Arc::clone(&guard_status),
141                final_spec.channel_usage(),
142            )
143            .await
144        {
145            Ok(circuit) => {
146                // Report success to the guard manager, so it knows that
147                // this guard is reachable.
148                guard_status.report(GuardStatus::Success);
149
150                // We have to wait for the guard manager to tell us whether
151                // this guard is actually _usable_ or not.  Possibly,
152                // it is a speculative guard that we're only trying out
153                // in case some preferable guard won't meet our needs.
154                match guard_usable.await {
155                    Some(Ok(true)) | None => (),
156                    Some(Ok(false)) => return Err(Error::GuardNotUsable(circuit.unique_id())),
157                    Some(Err(_)) => {
158                        return Err(internal!("Guard usability status cancelled").into());
159                    }
160                }
161                Ok((final_spec, circuit))
162            }
163            Err(e) => {
164                // The attempt failed; the builder should have set the
165                // pending status on the guard to some value which will
166                // tell the guard manager whether to blame the guard or not.
167                guard_status.commit();
168
169                Err(e)
170            }
171        }
172    }
173
174    fn launch_parallelism(&self, spec: &TargetCircUsage) -> usize {
175        match spec {
176            TargetCircUsage::Dir => 3,
177            _ => 1,
178        }
179    }
180
181    fn select_parallelism(&self, spec: &TargetCircUsage) -> usize {
182        self.launch_parallelism(spec)
183    }
184
185    fn learning_timeouts(&self) -> bool {
186        CircuitBuilder::learning_timeouts(self)
187    }
188
189    fn save_state(&self) -> Result<bool> {
190        CircuitBuilder::save_state(self)
191    }
192
193    fn path_config(&self) -> Arc<PathConfig> {
194        CircuitBuilder::path_config(self)
195    }
196
197    fn set_path_config(&self, new_config: PathConfig) {
198        CircuitBuilder::set_path_config(self, new_config);
199    }
200
201    fn estimator(&self) -> &timeouts::Estimator {
202        CircuitBuilder::estimator(self)
203    }
204
205    #[cfg(feature = "vanguards")]
206    fn vanguardmgr(&self) -> &Arc<VanguardMgr<R>> {
207        CircuitBuilder::vanguardmgr(self)
208    }
209
210    fn upgrade_to_owned_state(&self) -> Result<()> {
211        CircuitBuilder::upgrade_to_owned_state(self)
212    }
213
214    fn reload_state(&self) -> Result<()> {
215        CircuitBuilder::reload_state(self)
216    }
217
218    fn guardmgr(&self) -> &tor_guardmgr::GuardMgr<R> {
219        CircuitBuilder::guardmgr(self)
220    }
221
222    fn update_network_parameters(&self, p: &tor_netdir::params::NetParameters) {
223        CircuitBuilder::update_network_parameters(self, p);
224    }
225}