1
use crate::usage::{SupportedTunnelUsage, TargetTunnelUsage};
2
use crate::{DirInfo, Error, PathConfig, Result, timeouts};
3

            
4
#[cfg(feature = "vanguards")]
5
use tor_guardmgr::vanguards::VanguardMgr;
6
use tor_guardmgr::{GuardMgr, TestConfig, VanguardConfig};
7
use tor_linkspec::CircTarget;
8
use tor_persist::StateMgr;
9
use tor_proto::circuit::UniqId;
10
use tor_proto::client::circuit::{CircParameters, Path};
11
use tor_rtcompat::Runtime;
12

            
13
use async_trait::async_trait;
14
use std::sync::{self, Arc};
15
use std::time::Duration;
16

            
17
use crate::isolation::test::IsolationTokenEq;
18
use crate::usage::ExitPolicy;
19
use crate::{StreamIsolation, TargetPorts};
20
use std::sync::atomic::{self, AtomicUsize};
21
use tracing::trace;
22

            
23
use super::mgr::{AbstractTunnel, AbstractTunnelBuilder, MockablePlan};
24

            
25
#[derive(Debug, Clone, Eq, PartialEq, Hash, Copy)]
26
pub(crate) struct FakeId {
27
    pub(crate) id: usize,
28
}
29

            
30
static NEXT_FAKE_ID: AtomicUsize = AtomicUsize::new(0);
31
impl FakeId {
32
98
    pub(crate) fn next() -> Self {
33
98
        let id = NEXT_FAKE_ID.fetch_add(1, atomic::Ordering::SeqCst);
34
98
        FakeId { id }
35
98
    }
36
}
37

            
38
#[derive(Debug, PartialEq, Clone, Eq)]
39
pub(crate) struct FakeCirc {
40
    pub(crate) id: FakeId,
41
}
42

            
43
#[async_trait]
44
impl AbstractTunnel for FakeCirc {
45
    type Id = FakeId;
46
212
    fn id(&self) -> FakeId {
47
212
        self.id
48
212
    }
49
1324
    fn usable(&self) -> bool {
50
1324
        true
51
1324
    }
52

            
53
    fn single_path(&self) -> tor_proto::Result<Arc<Path>> {
54
        todo!()
55
    }
56

            
57
    fn n_hops(&self) -> tor_proto::Result<usize> {
58
        todo!()
59
    }
60

            
61
    fn is_closing(&self) -> bool {
62
        todo!()
63
    }
64

            
65
    fn unique_id(&self) -> UniqId {
66
        todo!()
67
    }
68

            
69
    async fn extend<T: CircTarget + Sync>(
70
        &self,
71
        _target: &T,
72
        _params: CircParameters,
73
    ) -> tor_proto::Result<()> {
74
        todo!()
75
    }
76

            
77
    async fn last_known_to_be_used_at(&self) -> tor_proto::Result<Option<std::time::Instant>> {
78
        Ok(None)
79
    }
80
}
81

            
82
#[derive(Debug, Clone)]
83
pub(crate) struct FakePlan {
84
    spec: SupportedTunnelUsage,
85
    op: FakeOp,
86
}
87

            
88
pub(crate) struct FakeBuilder<RT: Runtime> {
89
    runtime: RT,
90
    guardmgr: GuardMgr<RT>,
91
    #[cfg(feature = "vanguards")]
92
    vanguardmgr: Arc<VanguardMgr<RT>>,
93
    pub(crate) script: sync::Mutex<Vec<(TargetTunnelUsage, FakeOp)>>,
94
}
95

            
96
#[derive(Debug, Clone)]
97
pub(crate) enum FakeOp {
98
    Succeed,
99
    Fail,
100
    Delay(Duration),
101
    Timeout,
102
    TimeoutReleaseAdvance(String),
103
    NoPlan,
104
    WrongSpec(SupportedTunnelUsage),
105
}
106

            
107
impl MockablePlan for FakePlan {
108
136
    fn add_blocked_advance_reason(&mut self, reason: String) {
109
136
        if let FakeOp::Timeout = self.op {
110
8
            self.op = FakeOp::TimeoutReleaseAdvance(reason);
111
128
        }
112
136
    }
113
}
114

            
115
const FAKE_CIRC_DELAY: Duration = Duration::from_millis(30);
116

            
117
#[async_trait]
118
impl<RT: Runtime> AbstractTunnelBuilder<RT> for FakeBuilder<RT> {
119
    type Tunnel = FakeCirc;
120
    type Plan = FakePlan;
121

            
122
188
    fn plan_tunnel(
123
188
        &self,
124
188
        spec: &TargetTunnelUsage,
125
188
        _dir: DirInfo<'_>,
126
188
    ) -> Result<(FakePlan, SupportedTunnelUsage)> {
127
188
        let next_op = self.next_op(spec);
128
188
        if matches!(next_op, FakeOp::NoPlan) {
129
8
            return Err(Error::NoRelay {
130
8
                path_kind: "example",
131
8
                role: "example",
132
8
                problem: "called with no plan".to_string(),
133
8
            });
134
180
        }
135
180
        let supported_circ_usage = match spec {
136
            TargetTunnelUsage::Exit {
137
176
                ports,
138
176
                isolation,
139
176
                country_code,
140
176
                require_stability,
141
            } => SupportedTunnelUsage::Exit {
142
176
                policy: ExitPolicy::from_target_ports(&TargetPorts::from(&ports[..])),
143
176
                isolation: if isolation.isol_eq(&StreamIsolation::no_isolation()) {
144
168
                    None
145
                } else {
146
8
                    Some(isolation.clone())
147
                },
148
176
                country_code: *country_code,
149
176
                all_relays_stable: *require_stability,
150
            },
151
            #[cfg(feature = "hs-common")]
152
4
            TargetTunnelUsage::HsCircBase { .. } => SupportedTunnelUsage::HsOnly,
153
            _ => unimplemented!(),
154
        };
155
180
        let plan = FakePlan {
156
180
            spec: supported_circ_usage.clone(),
157
180
            op: next_op,
158
180
        };
159
180
        Ok((plan, supported_circ_usage))
160
188
    }
161

            
162
180
    async fn build_tunnel(&self, plan: FakePlan) -> Result<(SupportedTunnelUsage, FakeCirc)> {
163
        let op = plan.op;
164
        let sl = self.runtime.sleep(FAKE_CIRC_DELAY);
165
        self.runtime.allow_one_advance(FAKE_CIRC_DELAY);
166
        sl.await;
167
        match op {
168
            FakeOp::Succeed => Ok((plan.spec, FakeCirc { id: FakeId::next() })),
169
            FakeOp::WrongSpec(s) => Ok((s, FakeCirc { id: FakeId::next() })),
170
            FakeOp::Fail => Err(Error::CircTimeout(None)),
171
            FakeOp::Delay(d) => {
172
                let sl = self.runtime.sleep(d);
173
                self.runtime.allow_one_advance(d);
174
                sl.await;
175
                Err(Error::PendingCanceled)
176
            }
177
            FakeOp::Timeout => unreachable!(), // should be converted to the below
178
            FakeOp::TimeoutReleaseAdvance(reason) => {
179
                trace!("releasing advance to fake a timeout");
180
                self.runtime.release_advance(reason);
181
                let () = futures::future::pending().await;
182
                unreachable!()
183
            }
184
            FakeOp::NoPlan => unreachable!(),
185
        }
186
180
    }
187

            
188
56
    fn learning_timeouts(&self) -> bool {
189
56
        false
190
56
    }
191

            
192
4
    fn save_state(&self) -> Result<bool> {
193
        // We don't actually store persistent state since this is a test, just pretend we do.
194
4
        Ok(true)
195
4
    }
196

            
197
    fn path_config(&self) -> Arc<PathConfig> {
198
        todo!()
199
    }
200

            
201
    fn set_path_config(&self, _new_config: PathConfig) {
202
        todo!()
203
    }
204

            
205
    fn estimator(&self) -> &timeouts::Estimator {
206
        todo!()
207
    }
208

            
209
    #[cfg(feature = "vanguards")]
210
4
    fn vanguardmgr(&self) -> &Arc<VanguardMgr<RT>> {
211
4
        &self.vanguardmgr
212
4
    }
213

            
214
    fn upgrade_to_owned_state(&self) -> Result<()> {
215
        todo!()
216
    }
217

            
218
    fn reload_state(&self) -> Result<()> {
219
        todo!()
220
    }
221

            
222
4
    fn guardmgr(&self) -> &tor_guardmgr::GuardMgr<RT> {
223
4
        &self.guardmgr
224
4
    }
225

            
226
    fn update_network_parameters(&self, _p: &tor_netdir::params::NetParameters) {
227
        todo!()
228
    }
229
}
230

            
231
impl<RT: Runtime> FakeBuilder<RT> {
232
52
    pub(crate) fn new<S>(rt: &RT, state_mgr: S, guard_config: &TestConfig) -> Self
233
52
    where
234
52
        S: StateMgr + Send + Sync + 'static,
235
    {
236
52
        FakeBuilder {
237
52
            runtime: rt.clone(),
238
52
            guardmgr: GuardMgr::new(rt.clone(), state_mgr.clone(), guard_config)
239
52
                .expect("Create GuardMgr"),
240
52
            #[cfg(feature = "vanguards")]
241
52
            vanguardmgr: Arc::new(
242
52
                VanguardMgr::new(&VanguardConfig::default(), rt.clone(), state_mgr, false)
243
52
                    .expect("Create VanguardMgr"),
244
52
            ),
245
52
            script: sync::Mutex::new(vec![]),
246
52
        }
247
52
    }
248

            
249
    /// set a plan for a given TargetCircUsage.
250
28
    pub(crate) fn set<I>(&self, spec: &TargetTunnelUsage, v: I)
251
28
    where
252
28
        I: IntoIterator<Item = FakeOp>,
253
    {
254
28
        let mut ops: Vec<_> = v.into_iter().collect();
255
28
        ops.reverse();
256
28
        let mut lst = self.script.lock().expect("Couldn't get lock on script");
257
12060
        for op in ops {
258
12032
            lst.push((spec.clone(), op));
259
12032
        }
260
28
    }
261

            
262
188
    fn next_op(&self, spec: &TargetTunnelUsage) -> FakeOp {
263
188
        let mut script = self.script.lock().expect("Couldn't get lock on script");
264

            
265
188
        let idx = script
266
188
            .iter()
267
188
            .enumerate()
268
188
            .find_map(|(i, s)| spec.isol_eq(&s.0).then_some(i));
269

            
270
188
        if let Some(i) = idx {
271
92
            let (_, op) = script.remove(i);
272
92
            op
273
        } else {
274
96
            FakeOp::Succeed
275
        }
276
188
    }
277
}