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

            
3
use crate::build::TunnelBuilder;
4
use crate::mgr::{self, MockablePlan};
5
use crate::path::OwnedPath;
6
use crate::usage::{SupportedTunnelUsage, TargetTunnelUsage};
7
use crate::{DirInfo, Error, PathConfig, Result, timeouts};
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::{bad_api_usage, internal};
14
#[cfg(feature = "vanguards")]
15
use tor_guardmgr::vanguards::VanguardMgr;
16
use tor_linkspec::CircTarget;
17
use tor_proto::ClientTunnel;
18
use tor_proto::circuit::UniqId;
19
use tor_proto::client::circuit::{CircParameters, Path};
20
use tor_rtcompat::Runtime;
21
use tracing::instrument;
22

            
23
#[async_trait]
24
impl mgr::AbstractTunnel for tor_proto::ClientTunnel {
25
    type Id = tor_proto::circuit::UniqId;
26

            
27
    fn id(&self) -> Self::Id {
28
        self.unique_id()
29
    }
30

            
31
    fn usable(&self) -> bool {
32
        !self.is_closing()
33
    }
34

            
35
    // TODO: replace Itertools::exactly_one() with a stdlib equivalent when there is one.
36
    //
37
    // See issue #48919 <https://github.com/rust-lang/rust/issues/48919>
38
    #[allow(unstable_name_collisions)]
39
    fn single_path(&self) -> tor_proto::Result<Arc<Path>> {
40
        use itertools::Itertools as _;
41

            
42
        self.all_paths().into_iter().exactly_one().map_err(|_| {
43
            bad_api_usage!("requested the single path of a multi-path tunnel?!").into()
44
        })
45
    }
46

            
47
    fn n_hops(&self) -> tor_proto::Result<usize> {
48
        self.n_hops()
49
    }
50

            
51
    fn is_closing(&self) -> bool {
52
        self.is_closed()
53
    }
54

            
55
    fn unique_id(&self) -> UniqId {
56
        self.unique_id()
57
    }
58

            
59
    async fn extend<T: CircTarget + Sync>(
60
        &self,
61
        target: &T,
62
        params: CircParameters,
63
    ) -> tor_proto::Result<()> {
64
        let circ = self.as_single_circ()?;
65
        circ.extend(target, params).await
66
    }
67

            
68
    async fn last_known_to_be_used_at(&self) -> tor_proto::Result<Option<std::time::Instant>> {
69
        self.disused_since().await
70
    }
71
}
72

            
73
/// The information generated by circuit planning, and used to build a
74
/// circuit.
75
#[derive(Educe)]
76
#[educe(Debug)]
77
pub(crate) struct Plan {
78
    /// The supported usage that the circuit will have when complete
79
    final_spec: SupportedTunnelUsage,
80
    /// An owned copy of the path to build.
81
    // TODO: it would be nice if this weren't owned.
82
    path: OwnedPath,
83
    /// The protocol parameters to use when constructing the circuit.
84
    params: CircParameters,
85
    /// If this path is using a guard, we'll use this object to report
86
    /// whether the circuit succeeded or failed.
87
    guard_status: Option<tor_guardmgr::GuardMonitor>,
88
    /// If this path is using a guard, we'll use this object to learn
89
    /// whether we're allowed to use the circuit or whether we have to
90
    /// wait a while.
91
    #[educe(Debug(method = "skip_fmt"))]
92
    guard_usable: Option<tor_guardmgr::GuardUsable>,
93
}
94

            
95
impl MockablePlan for Plan {}
96

            
97
#[async_trait]
98
impl<R: Runtime> crate::mgr::AbstractTunnelBuilder<R> for crate::build::TunnelBuilder<R> {
99
    type Tunnel = ClientTunnel;
100
    type Plan = Plan;
101

            
102
    #[instrument(level = "trace", skip_all)]
103
    fn plan_tunnel(
104
        &self,
105
        usage: &TargetTunnelUsage,
106
        dir: DirInfo<'_>,
107
    ) -> Result<(Plan, SupportedTunnelUsage)> {
108
        let mut rng = rand::rng();
109
        let (path, final_spec, guard_status, guard_usable) = usage.build_path(
110
            &mut rng,
111
            dir,
112
            self.guardmgr(),
113
            #[cfg(all(feature = "vanguards", feature = "hs-common"))]
114
            self.vanguardmgr(),
115
            self.path_config().as_ref(),
116
            self.runtime().wallclock(),
117
        )?;
118

            
119
        let plan = Plan {
120
            final_spec: final_spec.clone(),
121
            path: (&path).try_into()?,
122
            params: dir.circ_params(usage)?,
123
            guard_status,
124
            guard_usable,
125
        };
126

            
127
        Ok((plan, final_spec))
128
    }
129

            
130
    #[instrument(level = "trace", skip_all)]
131
    async fn build_tunnel(&self, plan: Plan) -> Result<(SupportedTunnelUsage, Self::Tunnel)> {
132
        use crate::build::GuardStatusHandle;
133
        use tor_guardmgr::GuardStatus;
134
        let Plan {
135
            final_spec,
136
            path,
137
            params,
138
            guard_status,
139
            guard_usable,
140
        } = plan;
141

            
142
        let guard_usable: OptionFuture<_> = guard_usable.into();
143
        let guard_status: Arc<GuardStatusHandle> = Arc::new(guard_status.into());
144

            
145
        guard_status.pending(GuardStatus::AttemptAbandoned);
146

            
147
        // TODO: We may want to lower the logic for handling
148
        // guard_status and guard_usable into build.rs, so that they
149
        // can be handled correctly on user-selected paths as well.
150
        //
151
        // This will probably require a different API for circuit
152
        // construction.
153
        match self
154
            .build_owned(
155
                path,
156
                &params,
157
                Arc::clone(&guard_status),
158
                final_spec.channel_usage(),
159
            )
160
            .await
161
        {
162
            Ok(tunnel) => {
163
                // Report success to the guard manager, so it knows that
164
                // this guard is reachable.
165
                guard_status.report(GuardStatus::Success);
166

            
167
                // We have to wait for the guard manager to tell us whether
168
                // this guard is actually _usable_ or not.  Possibly,
169
                // it is a speculative guard that we're only trying out
170
                // in case some preferable guard won't meet our needs.
171
                match guard_usable.await {
172
                    Some(Ok(true)) | None => (),
173
                    Some(Ok(false)) => return Err(Error::GuardNotUsable(tunnel.unique_id())),
174
                    Some(Err(_)) => {
175
                        return Err(internal!("Guard usability status cancelled").into());
176
                    }
177
                }
178
                Ok((final_spec, tunnel))
179
            }
180
            Err(e) => {
181
                // The attempt failed; the builder should have set the
182
                // pending status on the guard to some value which will
183
                // tell the guard manager whether to blame the guard or not.
184
                guard_status.commit();
185

            
186
                Err(e)
187
            }
188
        }
189
    }
190

            
191
    fn launch_parallelism(&self, spec: &TargetTunnelUsage) -> usize {
192
        match spec {
193
            TargetTunnelUsage::Dir => 3,
194
            _ => 1,
195
        }
196
    }
197

            
198
    fn select_parallelism(&self, spec: &TargetTunnelUsage) -> usize {
199
        self.launch_parallelism(spec)
200
    }
201

            
202
    fn learning_timeouts(&self) -> bool {
203
        TunnelBuilder::learning_timeouts(self)
204
    }
205

            
206
46
    fn save_state(&self) -> Result<bool> {
207
46
        TunnelBuilder::save_state(self)
208
46
    }
209

            
210
8
    fn path_config(&self) -> Arc<PathConfig> {
211
8
        TunnelBuilder::path_config(self)
212
8
    }
213

            
214
4
    fn set_path_config(&self, new_config: PathConfig) {
215
4
        TunnelBuilder::set_path_config(self, new_config);
216
4
    }
217

            
218
    fn estimator(&self) -> &timeouts::Estimator {
219
        TunnelBuilder::estimator(self)
220
    }
221

            
222
    #[cfg(feature = "vanguards")]
223
38
    fn vanguardmgr(&self) -> &Arc<VanguardMgr<R>> {
224
38
        TunnelBuilder::vanguardmgr(self)
225
38
    }
226

            
227
    #[instrument(level = "trace", skip_all)]
228
    fn upgrade_to_owned_state(&self) -> Result<()> {
229
        TunnelBuilder::upgrade_to_owned_state(self)
230
    }
231

            
232
    #[instrument(level = "trace", skip_all)]
233
    fn reload_state(&self) -> Result<()> {
234
        TunnelBuilder::reload_state(self)
235
    }
236

            
237
48
    fn guardmgr(&self) -> &tor_guardmgr::GuardMgr<R> {
238
48
        TunnelBuilder::guardmgr(self)
239
48
    }
240

            
241
    fn update_network_parameters(&self, p: &tor_netdir::params::NetParameters) {
242
        TunnelBuilder::update_network_parameters(self, p);
243
    }
244
}