1
//! Implement traits from [`crate::mgr`] for the circuit types we use.
2

            
3
use crate::build::CircuitBuilder;
4
use crate::mgr::{self, MockablePlan};
5
use crate::path::OwnedPath;
6
use crate::usage::{SupportedCircUsage, TargetCircUsage};
7
use crate::{timeouts, DirInfo, Error, PathConfig, Result};
8
use async_trait::async_trait;
9
use educe::Educe;
10
use futures::future::OptionFuture;
11
use std::sync::Arc;
12
use tor_basic_utils::skip_fmt;
13
use tor_error::internal;
14
#[cfg(feature = "vanguards")]
15
use tor_guardmgr::vanguards::VanguardMgr;
16
use tor_linkspec::CircTarget;
17
use tor_proto::circuit::{CircParameters, ClientCirc, Path, UniqId};
18
use tor_rtcompat::Runtime;
19

            
20
#[async_trait]
21
impl 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<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)]
62
pub(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

            
80
impl MockablePlan for Plan {}
81

            
82
#[async_trait]
83
impl<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
24
    fn save_state(&self) -> Result<bool> {
190
24
        CircuitBuilder::save_state(self)
191
24
    }
192

            
193
4
    fn path_config(&self) -> Arc<PathConfig> {
194
4
        CircuitBuilder::path_config(self)
195
4
    }
196

            
197
2
    fn set_path_config(&self, new_config: PathConfig) {
198
2
        CircuitBuilder::set_path_config(self, new_config);
199
2
    }
200

            
201
    fn estimator(&self) -> &timeouts::Estimator {
202
        CircuitBuilder::estimator(self)
203
    }
204

            
205
    #[cfg(feature = "vanguards")]
206
22
    fn vanguardmgr(&self) -> &Arc<VanguardMgr<R>> {
207
22
        CircuitBuilder::vanguardmgr(self)
208
22
    }
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
18
    fn guardmgr(&self) -> &tor_guardmgr::GuardMgr<R> {
219
18
        CircuitBuilder::guardmgr(self)
220
18
    }
221

            
222
    fn update_network_parameters(&self, p: &tor_netdir::params::NetParameters) {
223
        CircuitBuilder::update_network_parameters(self, p);
224
    }
225
}