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