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 next_connect_handle: ConnectHandle,
143}
144
145impl MockTorClient {
146 pub fn new() -> MockTorClient {
148 let mut events: Vec<TorEvent> = Default::default();
149 let line = "[notice] MockTorClient running".to_string();
150 events.push(TorEvent::LogReceived { line });
151
152 let socket_addr = SocketAddr::from(([127, 0, 0, 1], 0u16));
153 let listener = TcpListener::bind(socket_addr).expect("tcplistener bind failed");
154
155 MockTorClient {
156 events,
157 bootstrapped: false,
158 client_auth_keys: Default::default(),
159 onion_services: Default::default(),
160 loopback: listener,
161 next_connect_handle: Default::default(),
162 }
163 }
164}
165
166impl Default for MockTorClient {
167 fn default() -> Self {
168 Self::new()
169 }
170}
171
172impl TorProvider for MockTorClient {
173 fn update(&mut self) -> Result<Vec<TorEvent>, tor_provider::Error> {
174 match MOCK_TOR_NETWORK.lock() {
175 Ok(mut mock_tor_network) => {
176 let mut i = 0;
177 while i < self.onion_services.len() {
178 if !self.onion_services[i].1.load(atomic::Ordering::Relaxed) {
180 let entry = self.onion_services.swap_remove(i);
181 let onion_addr = entry.0;
182 mock_tor_network.stop_onion(&onion_addr);
183 } else {
184 i += 1;
185 }
186 }
187 }
188 Err(_) => unreachable!("another thread panicked while holding mock tor network's lock"),
189 }
190
191 Ok(std::mem::take(&mut self.events))
192 }
193
194 fn bootstrap(&mut self) -> Result<(), tor_provider::Error> {
195 if self.bootstrapped {
196 Err(Error::ClientAlreadyBootstrapped())?
197 } else {
198 self.events.push(TorEvent::BootstrapStatus {
199 progress: 0u32,
200 tag: "start".to_string(),
201 summary: "bootstrapping started".to_string(),
202 });
203 self.events.push(TorEvent::BootstrapStatus {
204 progress: 50u32,
205 tag: "middle".to_string(),
206 summary: "bootstrapping continues".to_string(),
207 });
208 self.events.push(TorEvent::BootstrapStatus {
209 progress: 100u32,
210 tag: "finished".to_string(),
211 summary: "bootstrapping completed".to_string(),
212 });
213 self.events.push(TorEvent::BootstrapComplete);
214 self.bootstrapped = true;
215 Ok(())
216 }
217 }
218
219 fn add_client_auth(
220 &mut self,
221 service_id: &V3OnionServiceId,
222 client_auth: &X25519PrivateKey,
223 ) -> Result<(), tor_provider::Error> {
224 let client_auth_public = X25519PublicKey::from_private_key(client_auth);
225 if let Some(key) = self.client_auth_keys.get_mut(service_id) {
226 *key = client_auth_public;
227 } else {
228 self.client_auth_keys
229 .insert(service_id.clone(), client_auth_public);
230 }
231 Ok(())
232 }
233
234 fn remove_client_auth(
235 &mut self,
236 service_id: &V3OnionServiceId,
237 ) -> Result<(), tor_provider::Error> {
238 self.client_auth_keys.remove(service_id);
239 Ok(())
240 }
241
242 fn connect(
243 &mut self,
244 target: TargetAddr,
245 _circuit: Option<CircuitToken>,
246 ) -> Result<OnionStream, tor_provider::Error> {
247 let (service_id, virt_port) = match target {
248 TargetAddr::OnionService(OnionAddr::V3(OnionAddrV3 {
249 service_id,
250 virt_port,
251 })) => (service_id, virt_port),
252 target_address => {
253 if let Ok(stream) = TcpStream::connect(
254 self.loopback
255 .local_addr()
256 .expect("loopback local_addr failed"),
257 ) {
258 return Ok(OnionStream {
259 stream,
260 local_addr: None,
261 peer_addr: Some(target_address),
262 });
263 } else {
264 return Err(Error::ConnectFailed(target_address).into());
265 }
266 }
267 };
268 let client_auth = self.client_auth_keys.get(&service_id);
269
270 match MOCK_TOR_NETWORK.lock() {
271 Ok(mut mock_tor_network) => {
272 Ok(mock_tor_network.connect_to_onion(&service_id, virt_port, client_auth)?)
273 }
274 Err(_) => unreachable!("another thread panicked while holding mock tor network's lock"),
275 }
276 }
277
278 fn connect_async(
279 &mut self,
280 target: TargetAddr,
281 circuit: Option<CircuitToken>,
282 ) -> Result<ConnectHandle, tor_provider::Error> {
283
284 let handle = self.next_connect_handle;
285 self.next_connect_handle += 1usize;
286
287 let event = match self.connect(target, circuit) {
288 Ok(stream) => TorEvent::ConnectComplete{
289 handle,
290 stream,
291 },
292 Err(error) => TorEvent::ConnectFailed{
293 handle,
294 error,
295 },
296 };
297 self.events.push(event);
298
299 Ok(handle)
300 }
301
302 fn listener(
303 &mut self,
304 private_key: &Ed25519PrivateKey,
305 virt_port: u16,
306 authorized_clients: Option<&[X25519PublicKey]>,
307 ) -> Result<OnionListener, tor_provider::Error> {
308 let service_id = V3OnionServiceId::from_private_key(private_key);
310 let onion_addr = OnionAddr::V3(OnionAddrV3::new(service_id.clone(), virt_port));
311 let authorized_clients: Vec<X25519PublicKey> = match authorized_clients {
312 Some(keys) => keys.into(),
313 None => Default::default(),
314 };
315
316 let socket_addr = SocketAddr::from(([127, 0, 0, 1], 0u16));
318 let listener = TcpListener::bind(socket_addr).map_err(Error::TcpListenerBindFailed)?;
319 let socket_addr = listener
320 .local_addr()
321 .map_err(Error::TcpListenerLocalAddrFailed)?;
322
323 match MOCK_TOR_NETWORK.lock() {
325 Ok(mut mock_tor_network) => mock_tor_network.start_onion(
326 service_id.clone(),
327 virt_port,
328 authorized_clients,
329 socket_addr,
330 ),
331 Err(_) => unreachable!("another thread panicked while holding mock tor network's lock"),
332 }
333
334 let is_active = Arc::new(atomic::AtomicBool::new(true));
336 self.onion_services
337 .push((onion_addr.clone(), Arc::clone(&is_active)));
338
339 self.events
341 .push(TorEvent::OnionServicePublished { service_id });
342
343
344 Ok(OnionListener::new(listener, onion_addr, is_active, |is_active| {
345 is_active.store(false, atomic::Ordering::Relaxed);
346 }))
347 }
348
349 fn generate_token(&mut self) -> CircuitToken {
350 0usize
351 }
352
353 fn release_token(&mut self, _token: CircuitToken) {}
354}
355
356impl Drop for MockTorClient {
357 fn drop(&mut self) {
358 match MOCK_TOR_NETWORK.lock() {
360 Ok(mut mock_tor_network) => {
361 for entry in self.onion_services.iter() {
362 let onion_addr = &entry.0;
363 mock_tor_network.stop_onion(onion_addr);
364 }
365 }
366 Err(_) => unreachable!("another thread panicked while holding mock tor network's lock"),
367 }
368 }
369}