1
use crate::usage::{SupportedCircUsage, TargetCircUsage};
2
use crate::{timeouts, DirInfo, Error, PathConfig, Result};
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::{CircParameters, Path, UniqId};
10
use tor_rtcompat::Runtime;
11

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

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

            
22
use super::mgr::{AbstractCirc, AbstractCircBuilder, MockablePlan};
23

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

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

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

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

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

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

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

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

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

            
77
#[derive(Debug, Clone)]
78
pub(crate) struct FakePlan {
79
    spec: SupportedCircUsage,
80
    op: FakeOp,
81
}
82

            
83
pub(crate) struct FakeBuilder<RT: Runtime> {
84
    runtime: RT,
85
    guardmgr: GuardMgr<RT>,
86
    #[cfg(feature = "vanguards")]
87
    vanguardmgr: Arc<VanguardMgr<RT>>,
88
    pub(crate) script: sync::Mutex<Vec<(TargetCircUsage, FakeOp)>>,
89
}
90

            
91
#[derive(Debug, Clone)]
92
pub(crate) enum FakeOp {
93
    Succeed,
94
    Fail,
95
    Delay(Duration),
96
    Timeout,
97
    TimeoutReleaseAdvance(String),
98
    NoPlan,
99
    WrongSpec(SupportedCircUsage),
100
}
101

            
102
impl MockablePlan for FakePlan {
103
136
    fn add_blocked_advance_reason(&mut self, reason: String) {
104
136
        if let FakeOp::Timeout = self.op {
105
8
            self.op = FakeOp::TimeoutReleaseAdvance(reason);
106
128
        }
107
136
    }
108
}
109

            
110
const FAKE_CIRC_DELAY: Duration = Duration::from_millis(30);
111

            
112
#[async_trait]
113
impl<RT: Runtime> AbstractCircBuilder<RT> for FakeBuilder<RT> {
114
    type Circ = FakeCirc;
115
    type Plan = FakePlan;
116

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

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

            
183
52
    fn learning_timeouts(&self) -> bool {
184
52
        false
185
52
    }
186

            
187
4
    fn save_state(&self) -> Result<bool> {
188
4
        // We don't actually store persistent state since this is a test, just pretend we do.
189
4
        Ok(true)
190
4
    }
191

            
192
    fn path_config(&self) -> Arc<PathConfig> {
193
        todo!()
194
    }
195

            
196
    fn set_path_config(&self, _new_config: PathConfig) {
197
        todo!()
198
    }
199

            
200
    fn estimator(&self) -> &timeouts::Estimator {
201
        todo!()
202
    }
203

            
204
    #[cfg(feature = "vanguards")]
205
4
    fn vanguardmgr(&self) -> &Arc<VanguardMgr<RT>> {
206
4
        &self.vanguardmgr
207
4
    }
208

            
209
    fn upgrade_to_owned_state(&self) -> Result<()> {
210
        todo!()
211
    }
212

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

            
217
4
    fn guardmgr(&self) -> &tor_guardmgr::GuardMgr<RT> {
218
4
        &self.guardmgr
219
4
    }
220

            
221
    fn update_network_parameters(&self, _p: &tor_netdir::params::NetParameters) {
222
        todo!()
223
    }
224
}
225

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

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

            
257
188
    fn next_op(&self, spec: &TargetCircUsage) -> FakeOp {
258
188
        let mut script = self.script.lock().expect("Couldn't get lock on script");
259
188

            
260
188
        let idx = script
261
188
            .iter()
262
188
            .enumerate()
263
188
            .find_map(|(i, s)| spec.isol_eq(&s.0).then_some(i));
264

            
265
188
        if let Some(i) = idx {
266
92
            let (_, op) = script.remove(i);
267
92
            op
268
        } else {
269
96
            FakeOp::Succeed
270
        }
271
188
    }
272
}