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
use web_time_compat::Instant;
13

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
189
28
    fn learning_timeouts(&self) -> bool {
190
28
        false
191
28
    }
192

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

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

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

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

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

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

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

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

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

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

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

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

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

            
271
94
        if let Some(i) = idx {
272
46
            let (_, op) = script.remove(i);
273
46
            op
274
        } else {
275
48
            FakeOp::Succeed
276
        }
277
94
    }
278
}