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
98
    pub(crate) fn next() -> Self {
34
98
        let id = NEXT_FAKE_ID.fetch_add(1, atomic::Ordering::SeqCst);
35
98
        FakeId { id }
36
98
    }
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
212
    fn id(&self) -> FakeId {
48
212
        self.id
49
212
    }
50
1324
    fn usable(&self) -> bool {
51
1324
        true
52
1324
    }
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
136
    fn add_blocked_advance_reason(&mut self, reason: String) {
110
136
        if let FakeOp::Timeout = self.op {
111
8
            self.op = FakeOp::TimeoutReleaseAdvance(reason);
112
128
        }
113
136
    }
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
188
    fn plan_tunnel(
124
188
        &self,
125
188
        spec: &TargetTunnelUsage,
126
188
        _dir: DirInfo<'_>,
127
188
    ) -> Result<(FakePlan, SupportedTunnelUsage)> {
128
188
        let next_op = self.next_op(spec);
129
188
        if matches!(next_op, FakeOp::NoPlan) {
130
8
            return Err(Error::NoRelay {
131
8
                path_kind: "example",
132
8
                role: "example",
133
8
                problem: "called with no plan".to_string(),
134
8
            });
135
180
        }
136
180
        let supported_circ_usage = match spec {
137
            TargetTunnelUsage::Exit {
138
176
                ports,
139
176
                isolation,
140
176
                country_code,
141
176
                require_stability,
142
            } => SupportedTunnelUsage::Exit {
143
176
                policy: ExitPolicy::from_target_ports(&TargetPorts::from(&ports[..])),
144
176
                isolation: if isolation.isol_eq(&StreamIsolation::no_isolation()) {
145
168
                    None
146
                } else {
147
8
                    Some(isolation.clone())
148
                },
149
176
                country_code: *country_code,
150
176
                all_relays_stable: *require_stability,
151
            },
152
            #[cfg(feature = "hs-common")]
153
4
            TargetTunnelUsage::HsCircBase { .. } => SupportedTunnelUsage::HsOnly,
154
            _ => unimplemented!(),
155
        };
156
180
        let plan = FakePlan {
157
180
            spec: supported_circ_usage.clone(),
158
180
            op: next_op,
159
180
        };
160
180
        Ok((plan, supported_circ_usage))
161
188
    }
162

            
163
180
    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
180
    }
188

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

            
193
4
    fn save_state(&self) -> Result<bool> {
194
        // We don't actually store persistent state since this is a test, just pretend we do.
195
4
        Ok(true)
196
4
    }
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
4
    fn vanguardmgr(&self) -> &Arc<VanguardMgr<RT>> {
212
4
        &self.vanguardmgr
213
4
    }
214

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

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

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

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

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

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

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

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

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