1use std::collections::BTreeMap;
3use std::net::{SocketAddr, TcpListener, TcpStream};
4use std::sync::{atomic, Arc, Mutex};
5
6use crate::tor_crypto::*;
8use crate::tor_provider;
9use crate::tor_provider::*;
10
11
12#[derive(thiserror::Error, Debug)]
14pub enum Error {
15 #[error("client not bootstrapped")]
16 ClientNotBootstrapped(),
17
18 #[error("client already bootstrapped")]
19 ClientAlreadyBootstrapped(),
20
21 #[error("onion service not found: {}", .0)]
22 OnionServiceNotFound(OnionAddr),
23
24 #[error("onion service not published: {}", .0)]
25 OnionServiceNotPublished(OnionAddr),
26
27 #[error("onion service requires onion auth")]
28 OnionServiceRequiresOnionAuth(),
29
30 #[error("provided onion auth key invalid")]
31 OnionServiceAuthInvalid(),
32
33 #[error("unable to bind TCP listener")]
34 TcpListenerBindFailed(#[source] std::io::Error),
35
36 #[error("unable to get TCP listener's local adress")]
37 TcpListenerLocalAddrFailed(#[source] std::io::Error),
38
39 #[error("unable to connect to {}", .0)]
40 ConnectFailed(TargetAddr),
41
42 #[error("not implemented")]
43 NotImplemented(),
44}
45
46impl From<Error> for crate::tor_provider::Error {
47 fn from(error: Error) -> Self {
48 crate::tor_provider::Error::Generic(error.to_string())
49 }
50}
51
52struct MockTorNetwork {
53 onion_services: Option<BTreeMap<OnionAddr, (Vec<X25519PublicKey>, SocketAddr)>>,
54}
55
56impl MockTorNetwork {
57 const fn new() -> MockTorNetwork {
58 MockTorNetwork {
59 onion_services: None,
60 }
61 }
62
63 fn connect_to_onion(
64 &mut self,
65 service_id: &V3OnionServiceId,
66 virt_port: u16,
67 client_auth: Option<&X25519PublicKey>,
68 ) -> Result<OnionStream, Error> {
69 let onion_addr = OnionAddr::V3(OnionAddrV3::new(service_id.clone(), virt_port));
70
71 match &mut self.onion_services {
72 Some(onion_services) => {
73 if let Some((client_auth_keys, socket_addr)) = onion_services.get(&onion_addr) {
74 match (client_auth_keys.len(), client_auth) {
75 (0, None) => (),
76 (_, None) => return Err(Error::OnionServiceRequiresOnionAuth()),
77 (0, Some(_)) => return Err(Error::OnionServiceAuthInvalid()),
78 (_, Some(client_auth)) => {
79 if !client_auth_keys.contains(client_auth) {
80 return Err(Error::OnionServiceAuthInvalid());
81 }
82 }
83 }
84
85 if let Ok(stream) = TcpStream::connect(socket_addr) {
86 Ok(OnionStream {
87 stream,
88 local_addr: None,
89 peer_addr: Some(TargetAddr::OnionService(onion_addr)),
90 })
91 } else {
92 Err(Error::OnionServiceNotFound(onion_addr))
93 }
94 } else {
95 Err(Error::OnionServiceNotPublished(onion_addr))
96 }
97 }
98 None => Err(Error::OnionServiceNotPublished(onion_addr)),
99 }
100 }
101
102 fn start_onion(
103 &mut self,
104 service_id: V3OnionServiceId,
105 virt_port: u16,
106 client_auth_keys: Vec<X25519PublicKey>,
107 address: SocketAddr,
108 ) {
109 let onion_addr = OnionAddr::V3(OnionAddrV3::new(service_id, virt_port));
110 match &mut self.onion_services {
111 Some(onion_services) => {
112 onion_services.insert(onion_addr, (client_auth_keys, address));
113 }
114 None => {
115 let mut onion_services = BTreeMap::new();
116 onion_services.insert(onion_addr, (client_auth_keys, address));
117 self.onion_services = Some(onion_services);
118 }
119 }
120 }
121
122 fn stop_onion(&mut self, onion_addr: &OnionAddr) {
123 if let Some(onion_services) = &mut self.onion_services {
124 onion_services.remove(onion_addr);
125 }
126 }
127}
128
129static MOCK_TOR_NETWORK: Mutex<MockTorNetwork> = Mutex::new(MockTorNetwork::new());
130
131pub struct MockTorClient {
137 events: Vec<TorEvent>,
138 bootstrapped: bool,
139 client_auth_keys: BTreeMap<V3OnionServiceId, X25519PublicKey>,
140 onion_services: Vec<(OnionAddr, Arc<atomic::AtomicBool>)>,
141 loopback: TcpListener,
142}
143
144impl MockTorClient {
145 pub fn new() -> MockTorClient {
147 let mut events: Vec<TorEvent> = Default::default();
148 let line = "[notice] MockTorClient running".to_string();
149 events.push(TorEvent::LogReceived { line });
150
151 let socket_addr = SocketAddr::from(([127, 0, 0, 1], 0u16));
152 let listener = TcpListener::bind(socket_addr).expect("tcplistener bind failed");
153
154 MockTorClient {
155 events,
156 bootstrapped: false,
157 client_auth_keys: Default::default(),
158 onion_services: Default::default(),
159 loopback: listener,
160 }
161 }
162}
163
164impl Default for MockTorClient {
165 fn default() -> Self {
166 Self::new()
167 }
168}
169
170impl TorProvider for MockTorClient {
171 fn update(&mut self) -> Result<Vec<TorEvent>, tor_provider::Error> {
172 match MOCK_TOR_NETWORK.lock() {
173 Ok(mut mock_tor_network) => {
174 let mut i = 0;
175 while i < self.onion_services.len() {
176 if !self.onion_services[i].1.load(atomic::Ordering::Relaxed) {
178 let entry = self.onion_services.swap_remove(i);
179 let onion_addr = entry.0;
180 mock_tor_network.stop_onion(&onion_addr);
181 } else {
182 i += 1;
183 }
184 }
185 }
186 Err(_) => unreachable!("another thread panicked while holding mock tor network's lock"),
187 }
188
189 Ok(std::mem::take(&mut self.events))
190 }
191
192 fn bootstrap(&mut self) -> Result<(), tor_provider::Error> {
193 if self.bootstrapped {
194 Err(Error::ClientAlreadyBootstrapped())?
195 } else {
196 self.events.push(TorEvent::BootstrapStatus {
197 progress: 0u32,
198 tag: "start".to_string(),
199 summary: "bootstrapping started".to_string(),
200 });
201 self.events.push(TorEvent::BootstrapStatus {
202 progress: 50u32,
203 tag: "middle".to_string(),
204 summary: "bootstrapping continues".to_string(),
205 });
206 self.events.push(TorEvent::BootstrapStatus {
207 progress: 100u32,
208 tag: "finished".to_string(),
209 summary: "bootstrapping completed".to_string(),
210 });
211 self.events.push(TorEvent::BootstrapComplete);
212 self.bootstrapped = true;
213 Ok(())
214 }
215 }
216
217 fn add_client_auth(
218 &mut self,
219 service_id: &V3OnionServiceId,
220 client_auth: &X25519PrivateKey,
221 ) -> Result<(), tor_provider::Error> {
222 let client_auth_public = X25519PublicKey::from_private_key(client_auth);
223 if let Some(key) = self.client_auth_keys.get_mut(service_id) {
224 *key = client_auth_public;
225 } else {
226 self.client_auth_keys
227 .insert(service_id.clone(), client_auth_public);
228 }
229 Ok(())
230 }
231
232 fn remove_client_auth(
233 &mut self,
234 service_id: &V3OnionServiceId,
235 ) -> Result<(), tor_provider::Error> {
236 self.client_auth_keys.remove(service_id);
237 Ok(())
238 }
239
240 fn connect(
241 &mut self,
242 target: TargetAddr,
243 _circuit: Option<CircuitToken>,
244 ) -> Result<OnionStream, tor_provider::Error> {
245 let (service_id, virt_port) = match target {
246 TargetAddr::OnionService(OnionAddr::V3(OnionAddrV3 {
247 service_id,
248 virt_port,
249 })) => (service_id, virt_port),
250 target_address => {
251 if let Ok(stream) = TcpStream::connect(
252 self.loopback
253 .local_addr()
254 .expect("loopback local_addr failed"),
255 ) {
256 return Ok(OnionStream {
257 stream,
258 local_addr: None,
259 peer_addr: Some(target_address),
260 });
261 } else {
262 return Err(Error::ConnectFailed(target_address).into());
263 }
264 }
265 };
266 let client_auth = self.client_auth_keys.get(&service_id);
267
268 match MOCK_TOR_NETWORK.lock() {
269 Ok(mut mock_tor_network) => {
270 Ok(mock_tor_network.connect_to_onion(&service_id, virt_port, client_auth)?)
271 }
272 Err(_) => unreachable!("another thread panicked while holding mock tor network's lock"),
273 }
274 }
275
276 fn listener(
277 &mut self,
278 private_key: &Ed25519PrivateKey,
279 virt_port: u16,
280 authorized_clients: Option<&[X25519PublicKey]>,
281 ) -> Result<OnionListener, tor_provider::Error> {
282 let service_id = V3OnionServiceId::from_private_key(private_key);
284 let onion_addr = OnionAddr::V3(OnionAddrV3::new(service_id.clone(), virt_port));
285 let authorized_clients: Vec<X25519PublicKey> = match authorized_clients {
286 Some(keys) => keys.into(),
287 None => Default::default(),
288 };
289
290 let socket_addr = SocketAddr::from(([127, 0, 0, 1], 0u16));
292 let listener = TcpListener::bind(socket_addr).map_err(Error::TcpListenerBindFailed)?;
293 let socket_addr = listener
294 .local_addr()
295 .map_err(Error::TcpListenerLocalAddrFailed)?;
296
297 match MOCK_TOR_NETWORK.lock() {
299 Ok(mut mock_tor_network) => mock_tor_network.start_onion(
300 service_id.clone(),
301 virt_port,
302 authorized_clients,
303 socket_addr,
304 ),
305 Err(_) => unreachable!("another thread panicked while holding mock tor network's lock"),
306 }
307
308 let is_active = Arc::new(atomic::AtomicBool::new(true));
310 self.onion_services
311 .push((onion_addr.clone(), Arc::clone(&is_active)));
312
313 self.events
315 .push(TorEvent::OnionServicePublished { service_id });
316
317
318 Ok(OnionListener::new(listener, onion_addr, is_active, |is_active| {
319 is_active.store(false, atomic::Ordering::Relaxed);
320 }))
321 }
322
323 fn generate_token(&mut self) -> CircuitToken {
324 0usize
325 }
326
327 fn release_token(&mut self, _token: CircuitToken) {}
328}
329
330impl Drop for MockTorClient {
331 fn drop(&mut self) {
332 match MOCK_TOR_NETWORK.lock() {
334 Ok(mut mock_tor_network) => {
335 for entry in self.onion_services.iter() {
336 let onion_addr = &entry.0;
337 mock_tor_network.stop_onion(onion_addr);
338 }
339 }
340 Err(_) => unreachable!("another thread panicked while holding mock tor network's lock"),
341 }
342 }
343}