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

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

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

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

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

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

            
113
#[async_trait]
114
impl<RT: Runtime> AbstractTunnelBuilder<RT> for FakeBuilder<RT> {
115
    type Tunnel = FakeCirc;
116
    type Plan = FakePlan;
117

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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