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

            
3
use crate::mgr::{self, MockablePlan};
4
use crate::path::OwnedPath;
5
use crate::usage::{SupportedCircUsage, TargetCircUsage};
6
use crate::{DirInfo, Error, Result};
7
use async_trait::async_trait;
8
use educe::Educe;
9
use futures::future::OptionFuture;
10
use std::convert::TryInto;
11
use std::sync::Arc;
12
use tor_basic_utils::skip_fmt;
13
use tor_error::internal;
14
use tor_proto::circuit::{CircParameters, ClientCirc};
15
use tor_rtcompat::Runtime;
16

            
17
impl mgr::AbstractCirc for tor_proto::circuit::ClientCirc {
18
    type Id = tor_proto::circuit::UniqId;
19
    fn id(&self) -> Self::Id {
20
        self.unique_id()
21
    }
22
    fn usable(&self) -> bool {
23
        !self.is_closing()
24
    }
25
}
26

            
27
/// The information generated by circuit planning, and used to build a
28
/// circuit.
29
#[derive(Educe)]
30
#[educe(Debug)]
31
pub(crate) struct Plan {
32
    /// The supported usage that the circuit will have when complete
33
    final_spec: SupportedCircUsage,
34
    /// An owned copy of the path to build.
35
    // TODO: it would be nice if this weren't owned.
36
    path: OwnedPath,
37
    /// The protocol parameters to use when constructing the circuit.
38
    params: CircParameters,
39
    /// If this path is using a guard, we'll use this object to report
40
    /// whether the circuit succeeded or failed.
41
    guard_status: Option<tor_guardmgr::GuardMonitor>,
42
    /// If this path is using a guard, we'll use this object to learn
43
    /// whether we're allowed to use the circuit or whether we have to
44
    /// wait a while.
45
    #[educe(Debug(method = "skip_fmt"))]
46
    guard_usable: Option<tor_guardmgr::GuardUsable>,
47
}
48

            
49
impl MockablePlan for Plan {}
50

            
51
#[async_trait]
52
impl<R: Runtime> crate::mgr::AbstractCircBuilder for crate::build::CircuitBuilder<R> {
53
    type Circ = ClientCirc;
54
    type Spec = SupportedCircUsage;
55
    type Plan = Plan;
56

            
57
    fn plan_circuit(
58
        &self,
59
        usage: &TargetCircUsage,
60
        dir: DirInfo<'_>,
61
    ) -> Result<(Plan, SupportedCircUsage)> {
62
        let mut rng = rand::thread_rng();
63
        let (path, final_spec, guard_status, guard_usable) = usage.build_path(
64
            &mut rng,
65
            dir,
66
            Some(self.guardmgr()),
67
            self.path_config().as_ref(),
68
        )?;
69

            
70
        let plan = Plan {
71
            final_spec: final_spec.clone(),
72
            path: (&path).try_into()?,
73
            params: dir.circ_params(),
74
            guard_status,
75
            guard_usable,
76
        };
77

            
78
        Ok((plan, final_spec))
79
    }
80

            
81
    async fn build_circuit(&self, plan: Plan) -> Result<(SupportedCircUsage, ClientCirc)> {
82
        use crate::build::GuardStatusHandle;
83
        use tor_guardmgr::GuardStatus;
84
        let Plan {
85
            final_spec,
86
            path,
87
            params,
88
            guard_status,
89
            guard_usable,
90
        } = plan;
91

            
92
        let guard_usable: OptionFuture<_> = guard_usable.into();
93
        let guard_status: Arc<GuardStatusHandle> = Arc::new(guard_status.into());
94

            
95
        guard_status.pending(GuardStatus::AttemptAbandoned);
96

            
97
        // TODO: We may want to lower the logic for handling
98
        // guard_status and guard_usable into build.rs, so that they
99
        // can be handled correctly on user-selected paths as well.
100
        //
101
        // This will probably require a different API for circuit
102
        // construction.
103
        match self
104
            .build_owned(path, &params, Arc::clone(&guard_status))
105
            .await
106
        {
107
            Ok(circuit) => {
108
                // Report success to the guard manager, so it knows that
109
                // this guard is reachable.
110
                guard_status.report(GuardStatus::Success);
111

            
112
                // We have to wait for the guard manager to tell us whether
113
                // this guard is actually _usable_ or not.  Possibly,
114
                // it is a speculative guard that we're only trying out
115
                // in case some preferable guard won't meet our needs.
116
                match guard_usable.await {
117
                    Some(Ok(true)) | None => (),
118
                    Some(Ok(false)) => return Err(Error::GuardNotUsable),
119
                    Some(Err(_)) => {
120
                        return Err(internal!("Guard usability status cancelled").into());
121
                    }
122
                }
123
                Ok((final_spec, circuit))
124
            }
125
            Err(e) => {
126
                // The attempt failed; the builder should have set the
127
                // pending status on the guard to some value which will
128
                // tell the guard manager whether to blame the guard or not.
129
                guard_status.commit();
130

            
131
                Err(e)
132
            }
133
        }
134
    }
135

            
136
    fn launch_parallelism(&self, spec: &TargetCircUsage) -> usize {
137
        match spec {
138
            TargetCircUsage::Dir => 3,
139
            _ => 1,
140
        }
141
    }
142

            
143
    fn select_parallelism(&self, spec: &TargetCircUsage) -> usize {
144
        self.launch_parallelism(spec)
145
    }
146

            
147
    fn learning_timeouts(&self) -> bool {
148
        crate::build::CircuitBuilder::learning_timeouts(self)
149
    }
150
}