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