Skip to main content

ipfrs_network/
tor.rs

1//! Tor Integration for Privacy-Preserving Networking
2//!
3//! This module provides integration with the Tor network for anonymous and privacy-preserving
4//! peer-to-peer communication. It supports both client connections through Tor and hosting
5//! hidden services for anonymous server endpoints.
6//!
7//! ## Features
8//!
9//! - **SOCKS5 Proxy**: Connect to peers through Tor's SOCKS5 proxy
10//! - **Onion Routing**: Multi-hop encrypted routing for anonymity
11//! - **Hidden Services**: Host anonymous .onion endpoints
12//! - **Circuit Management**: Control Tor circuits for optimal performance
13//! - **Stream Isolation**: Separate streams for different applications
14//! - **Bandwidth Management**: Throttle Tor traffic to avoid network congestion
15//!
16//! ## Use Cases
17//!
18//! - **Privacy**: Hide IP addresses from peers and network observers
19//! - **Censorship Resistance**: Access content in restrictive networks
20//! - **Anonymous Publishing**: Host content without revealing location
21//! - **Surveillance Protection**: Protect against traffic analysis
22//!
23//! ## Example
24//!
25//! ```rust,no_run
26//! use ipfrs_network::tor::{TorManager, TorConfig, HiddenServiceConfig};
27//!
28//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
29//! // Create Tor manager
30//! let config = TorConfig::default();
31//! let mut manager = TorManager::new(config).await?;
32//!
33//! // Start Tor
34//! manager.start().await?;
35//!
36//! // Connect through Tor
37//! let peer_addr = "example.onion:8080";
38//! let stream = manager.connect(peer_addr).await?;
39//!
40//! // Create hidden service
41//! let hs_config = HiddenServiceConfig::default();
42//! let onion_addr = manager.create_hidden_service(hs_config).await?;
43//! println!("Hidden service available at: {}", onion_addr);
44//! # Ok(())
45//! # }
46//! ```
47
48use dashmap::DashMap;
49use parking_lot::RwLock;
50use std::collections::HashMap;
51use std::net::SocketAddr;
52use std::path::PathBuf;
53use std::sync::Arc;
54use std::time::{Duration, Instant};
55use thiserror::Error;
56use tracing::{debug, info};
57
58/// Errors that can occur in Tor operations
59#[derive(Debug, Error)]
60pub enum TorError {
61    #[error("Tor not running")]
62    NotRunning,
63
64    #[error("Tor already running")]
65    AlreadyRunning,
66
67    #[error("Connection failed: {0}")]
68    ConnectionFailed(String),
69
70    #[error("Hidden service creation failed: {0}")]
71    HiddenServiceFailed(String),
72
73    #[error("Circuit creation failed: {0}")]
74    CircuitFailed(String),
75
76    #[error("Invalid onion address: {0}")]
77    InvalidOnionAddress(String),
78
79    #[error("SOCKS5 proxy error: {0}")]
80    Socks5Error(String),
81
82    #[error("Tor configuration error: {0}")]
83    ConfigError(String),
84
85    #[error("IO error: {0}")]
86    Io(#[from] std::io::Error),
87}
88
89/// Result type for Tor operations
90pub type Result<T> = std::result::Result<T, TorError>;
91
92/// Tor circuit identifier
93pub type CircuitId = u32;
94
95/// Stream identifier
96pub type StreamId = u64;
97
98/// Onion address (e.g., "example.onion")
99pub type OnionAddress = String;
100
101/// Tor circuit state
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub enum CircuitState {
104    /// Circuit is being built
105    Building,
106
107    /// Circuit is ready for use
108    Ready,
109
110    /// Circuit is being used
111    Active,
112
113    /// Circuit is degraded (slow or unreliable)
114    Degraded,
115
116    /// Circuit has failed
117    Failed,
118
119    /// Circuit is being closed
120    Closing,
121}
122
123/// Information about a Tor circuit
124#[derive(Debug, Clone)]
125pub struct CircuitInfo {
126    /// Circuit identifier
127    pub id: CircuitId,
128
129    /// Circuit state
130    pub state: CircuitState,
131
132    /// Relay nodes in the circuit (3 hops typically)
133    pub hops: Vec<String>,
134
135    /// When the circuit was created
136    pub created_at: Instant,
137
138    /// Number of streams using this circuit
139    pub stream_count: usize,
140
141    /// Total bytes sent through this circuit
142    pub bytes_sent: u64,
143
144    /// Total bytes received through this circuit
145    pub bytes_received: u64,
146}
147
148/// Hidden service configuration
149#[derive(Debug, Clone)]
150pub struct HiddenServiceConfig {
151    /// Local port to expose
152    pub local_port: u16,
153
154    /// Virtual port (port visible to Tor users)
155    pub virtual_port: u16,
156
157    /// Directory for hidden service keys
158    pub data_dir: PathBuf,
159
160    /// Maximum concurrent connections
161    pub max_connections: usize,
162
163    /// Enable v3 onion addresses (recommended)
164    pub use_v3: bool,
165}
166
167impl Default for HiddenServiceConfig {
168    fn default() -> Self {
169        Self {
170            local_port: 8080,
171            virtual_port: 8080,
172            data_dir: std::env::temp_dir().join("tor-hidden-service"),
173            max_connections: 100,
174            use_v3: true,
175        }
176    }
177}
178
179/// Configuration for Tor manager
180#[derive(Debug, Clone)]
181pub struct TorConfig {
182    /// SOCKS5 proxy address (default: 127.0.0.1:9050)
183    pub socks_proxy: SocketAddr,
184
185    /// Control port address (default: 127.0.0.1:9051)
186    pub control_port: SocketAddr,
187
188    /// Tor data directory
189    pub data_dir: PathBuf,
190
191    /// Enable stream isolation (separate circuits per stream)
192    pub stream_isolation: bool,
193
194    /// Maximum circuits to maintain
195    pub max_circuits: usize,
196
197    /// Circuit timeout duration
198    pub circuit_timeout: Duration,
199
200    /// Enable bandwidth limiting
201    pub enable_bandwidth_limit: bool,
202
203    /// Maximum bandwidth in bytes/sec (0 = unlimited)
204    pub max_bandwidth_bps: u64,
205
206    /// Use bridges for censorship circumvention
207    pub use_bridges: bool,
208
209    /// Bridge addresses (if use_bridges is true)
210    pub bridges: Vec<String>,
211}
212
213impl Default for TorConfig {
214    fn default() -> Self {
215        Self {
216            socks_proxy: "127.0.0.1:9050"
217                .parse()
218                .expect("static socket addr literal must parse"),
219            control_port: "127.0.0.1:9051"
220                .parse()
221                .expect("static socket addr literal must parse"),
222            data_dir: std::env::temp_dir().join("tor-data"),
223            stream_isolation: true,
224            max_circuits: 10,
225            circuit_timeout: Duration::from_secs(60),
226            enable_bandwidth_limit: false,
227            max_bandwidth_bps: 0,
228            use_bridges: false,
229            bridges: Vec::new(),
230        }
231    }
232}
233
234impl TorConfig {
235    /// Configuration for high-privacy mode
236    pub fn high_privacy() -> Self {
237        Self {
238            stream_isolation: true,
239            max_circuits: 5,
240            circuit_timeout: Duration::from_secs(90),
241            enable_bandwidth_limit: true,
242            max_bandwidth_bps: 1_000_000, // 1 MB/s
243            ..Default::default()
244        }
245    }
246
247    /// Configuration for high-performance mode
248    pub fn high_performance() -> Self {
249        Self {
250            stream_isolation: false,
251            max_circuits: 20,
252            circuit_timeout: Duration::from_secs(30),
253            enable_bandwidth_limit: false,
254            max_bandwidth_bps: 0,
255            ..Default::default()
256        }
257    }
258
259    /// Configuration for censorship circumvention
260    pub fn censorship_resistant() -> Self {
261        Self {
262            use_bridges: true,
263            bridges: vec![
264                // Example bridges (should be updated with real bridge addresses)
265                "obfs4 192.0.2.1:443".to_string(),
266                "obfs4 192.0.2.2:443".to_string(),
267            ],
268            stream_isolation: true,
269            max_circuits: 8,
270            circuit_timeout: Duration::from_secs(120),
271            ..Default::default()
272        }
273    }
274}
275
276/// Statistics for Tor operations
277#[derive(Debug, Clone, Default)]
278pub struct TorStats {
279    /// Total circuits created
280    pub circuits_created: usize,
281
282    /// Currently active circuits
283    pub active_circuits: usize,
284
285    /// Total streams created
286    pub streams_created: usize,
287
288    /// Currently active streams
289    pub active_streams: usize,
290
291    /// Total bytes sent through Tor
292    pub total_bytes_sent: u64,
293
294    /// Total bytes received through Tor
295    pub total_bytes_received: u64,
296
297    /// Number of hidden services hosted
298    pub hidden_services: usize,
299
300    /// Total connection failures
301    pub connection_failures: usize,
302
303    /// Average circuit build time (ms)
304    pub avg_circuit_build_time_ms: f64,
305}
306
307/// Tor network manager
308pub struct TorManager {
309    /// Configuration
310    config: TorConfig,
311
312    /// Running state
313    running: Arc<RwLock<bool>>,
314
315    /// Active circuits
316    circuits: Arc<DashMap<CircuitId, CircuitInfo>>,
317
318    /// Next circuit ID
319    next_circuit_id: Arc<RwLock<CircuitId>>,
320
321    /// Stream to circuit mapping
322    stream_circuits: Arc<DashMap<StreamId, CircuitId>>,
323
324    /// Next stream ID
325    next_stream_id: Arc<RwLock<StreamId>>,
326
327    /// Hidden services
328    hidden_services: Arc<DashMap<OnionAddress, HiddenServiceConfig>>,
329
330    /// Statistics
331    stats: Arc<RwLock<TorStats>>,
332
333    /// Circuit build times (for averaging)
334    circuit_build_times: Arc<RwLock<Vec<f64>>>,
335}
336
337impl TorManager {
338    /// Create a new Tor manager
339    pub async fn new(config: TorConfig) -> Result<Self> {
340        info!("Creating Tor manager");
341
342        // Validate configuration
343        if config.max_circuits == 0 {
344            return Err(TorError::ConfigError(
345                "max_circuits must be > 0".to_string(),
346            ));
347        }
348
349        Ok(Self {
350            config,
351            running: Arc::new(RwLock::new(false)),
352            circuits: Arc::new(DashMap::new()),
353            next_circuit_id: Arc::new(RwLock::new(0)),
354            stream_circuits: Arc::new(DashMap::new()),
355            next_stream_id: Arc::new(RwLock::new(0)),
356            hidden_services: Arc::new(DashMap::new()),
357            stats: Arc::new(RwLock::new(TorStats::default())),
358            circuit_build_times: Arc::new(RwLock::new(Vec::with_capacity(100))),
359        })
360    }
361
362    /// Start the Tor manager
363    pub async fn start(&mut self) -> Result<()> {
364        let mut running = self.running.write();
365        if *running {
366            return Err(TorError::AlreadyRunning);
367        }
368
369        info!("Starting Tor manager");
370        info!("SOCKS proxy: {}", self.config.socks_proxy);
371        info!("Control port: {}", self.config.control_port);
372
373        // In a real implementation, this would:
374        // 1. Start Tor process or connect to existing Tor daemon
375        // 2. Authenticate with control port
376        // 3. Configure Tor settings
377        // 4. Wait for bootstrap completion
378
379        *running = true;
380        info!("Tor manager started");
381
382        Ok(())
383    }
384
385    /// Stop the Tor manager
386    pub async fn stop(&mut self) -> Result<()> {
387        let mut running = self.running.write();
388        if !*running {
389            return Err(TorError::NotRunning);
390        }
391
392        info!("Stopping Tor manager");
393
394        // Close all circuits
395        for circuit in self.circuits.iter() {
396            let circuit_id = circuit.key();
397            debug!("Closing circuit {}", circuit_id);
398        }
399        self.circuits.clear();
400
401        // Remove all hidden services
402        self.hidden_services.clear();
403
404        *running = false;
405        info!("Tor manager stopped");
406
407        Ok(())
408    }
409
410    /// Check if Tor is running
411    pub fn is_running(&self) -> bool {
412        *self.running.read()
413    }
414
415    /// Create a new Tor circuit
416    pub async fn create_circuit(&self) -> Result<CircuitId> {
417        if !self.is_running() {
418            return Err(TorError::NotRunning);
419        }
420
421        if self.circuits.len() >= self.config.max_circuits {
422            // Remove oldest inactive circuit
423            self.cleanup_circuits();
424
425            if self.circuits.len() >= self.config.max_circuits {
426                return Err(TorError::CircuitFailed(
427                    "Maximum circuits reached".to_string(),
428                ));
429            }
430        }
431
432        let circuit_id = {
433            let mut id = self.next_circuit_id.write();
434            let current_id = *id;
435            *id += 1;
436            current_id
437        };
438
439        let start_time = Instant::now();
440
441        // In a real implementation, this would:
442        // 1. Select guard, middle, and exit nodes
443        // 2. Build circuit through Tor control protocol
444        // 3. Wait for circuit to be ready
445
446        // Simulate circuit with 3 hops
447        let circuit = CircuitInfo {
448            id: circuit_id,
449            state: CircuitState::Ready,
450            hops: vec![
451                "GuardNode".to_string(),
452                "MiddleNode".to_string(),
453                "ExitNode".to_string(),
454            ],
455            created_at: Instant::now(),
456            stream_count: 0,
457            bytes_sent: 0,
458            bytes_received: 0,
459        };
460
461        self.circuits.insert(circuit_id, circuit);
462
463        // Record build time
464        let build_time_ms = start_time.elapsed().as_secs_f64() * 1000.0;
465        let mut build_times = self.circuit_build_times.write();
466        build_times.push(build_time_ms);
467        if build_times.len() > 100 {
468            build_times.remove(0);
469        }
470
471        let mut stats = self.stats.write();
472        stats.circuits_created += 1;
473        stats.active_circuits = self.circuits.len();
474        stats.avg_circuit_build_time_ms =
475            build_times.iter().sum::<f64>() / build_times.len() as f64;
476
477        info!("Created circuit {} in {:.1}ms", circuit_id, build_time_ms);
478
479        Ok(circuit_id)
480    }
481
482    /// Connect to an address through Tor
483    pub async fn connect(&self, address: &str) -> Result<StreamId> {
484        if !self.is_running() {
485            return Err(TorError::NotRunning);
486        }
487
488        // Get or create a circuit
489        let circuit_id = if self.config.stream_isolation {
490            // Create new circuit for stream isolation
491            self.create_circuit().await?
492        } else {
493            // Reuse existing circuit
494            self.get_or_create_circuit().await?
495        };
496
497        let stream_id = {
498            let mut id = self.next_stream_id.write();
499            let current_id = *id;
500            *id += 1;
501            current_id
502        };
503
504        // In a real implementation, this would:
505        // 1. Open SOCKS5 connection to Tor proxy
506        // 2. Send SOCKS5 connect request
507        // 3. Wait for connection establishment
508        // 4. Return stream handle
509
510        // Map stream to circuit
511        self.stream_circuits.insert(stream_id, circuit_id);
512
513        // Update circuit
514        if let Some(mut circuit) = self.circuits.get_mut(&circuit_id) {
515            circuit.stream_count += 1;
516            circuit.state = CircuitState::Active;
517        }
518
519        let mut stats = self.stats.write();
520        stats.streams_created += 1;
521        stats.active_streams = self.stream_circuits.len();
522
523        debug!("Connected to {} via circuit {}", address, circuit_id);
524
525        Ok(stream_id)
526    }
527
528    /// Create a hidden service
529    pub async fn create_hidden_service(&self, config: HiddenServiceConfig) -> Result<OnionAddress> {
530        if !self.is_running() {
531            return Err(TorError::NotRunning);
532        }
533
534        // In a real implementation, this would:
535        // 1. Generate hidden service keys (v3 onion address)
536        // 2. Configure Tor to host hidden service
537        // 3. Wait for descriptor publication
538        // 4. Return .onion address
539
540        // Generate a mock v3 onion address (56 characters, base32: a-z, 2-7)
541        let onion_addr = if config.use_v3 {
542            format!(
543                "{}.onion",
544                "abcdefghijklmnopqrstuvabcdefghijklmnopqrstuvabcdefghijkl"
545            )
546        } else {
547            // v2 onion address (16 characters) - deprecated
548            format!("{}.onion", "abcdefghijklmnop")
549        };
550
551        self.hidden_services.insert(onion_addr.clone(), config);
552
553        let mut stats = self.stats.write();
554        stats.hidden_services = self.hidden_services.len();
555
556        info!("Created hidden service: {}", onion_addr);
557
558        Ok(onion_addr)
559    }
560
561    /// Remove a hidden service
562    pub async fn remove_hidden_service(&self, onion_addr: &str) -> Result<()> {
563        if !self.is_running() {
564            return Err(TorError::NotRunning);
565        }
566
567        if self.hidden_services.remove(onion_addr).is_some() {
568            let mut stats = self.stats.write();
569            stats.hidden_services = self.hidden_services.len();
570
571            info!("Removed hidden service: {}", onion_addr);
572            Ok(())
573        } else {
574            Err(TorError::InvalidOnionAddress(onion_addr.to_string()))
575        }
576    }
577
578    /// Get or create a circuit
579    async fn get_or_create_circuit(&self) -> Result<CircuitId> {
580        // Try to find a ready circuit with low stream count
581        let best_circuit = self
582            .circuits
583            .iter()
584            .filter(|entry| {
585                let circuit = entry.value();
586                circuit.state == CircuitState::Ready && circuit.stream_count < 10
587            })
588            .min_by_key(|entry| entry.value().stream_count)
589            .map(|entry| *entry.key());
590
591        if let Some(circuit_id) = best_circuit {
592            Ok(circuit_id)
593        } else {
594            self.create_circuit().await
595        }
596    }
597
598    /// Cleanup old circuits
599    fn cleanup_circuits(&self) {
600        let now = Instant::now();
601        let timeout = self.config.circuit_timeout;
602
603        let old_circuits: Vec<CircuitId> = self
604            .circuits
605            .iter()
606            .filter(|entry| {
607                let circuit = entry.value();
608                circuit.stream_count == 0 && now.duration_since(circuit.created_at) > timeout
609            })
610            .map(|entry| *entry.key())
611            .collect();
612
613        for circuit_id in old_circuits {
614            debug!("Removing old circuit {}", circuit_id);
615            self.circuits.remove(&circuit_id);
616        }
617
618        let mut stats = self.stats.write();
619        stats.active_circuits = self.circuits.len();
620    }
621
622    /// Close a stream
623    pub fn close_stream(&self, stream_id: StreamId) -> Result<()> {
624        if let Some((_, circuit_id)) = self.stream_circuits.remove(&stream_id) {
625            // Update circuit stream count
626            if let Some(mut circuit) = self.circuits.get_mut(&circuit_id) {
627                circuit.stream_count = circuit.stream_count.saturating_sub(1);
628
629                if circuit.stream_count == 0 {
630                    circuit.state = CircuitState::Ready;
631                }
632            }
633
634            let mut stats = self.stats.write();
635            stats.active_streams = self.stream_circuits.len();
636
637            debug!("Closed stream {}", stream_id);
638            Ok(())
639        } else {
640            Err(TorError::ConnectionFailed(format!(
641                "Stream {} not found",
642                stream_id
643            )))
644        }
645    }
646
647    /// Get circuit information
648    pub fn get_circuit(&self, circuit_id: CircuitId) -> Option<CircuitInfo> {
649        self.circuits.get(&circuit_id).map(|e| e.value().clone())
650    }
651
652    /// Get all circuits
653    pub fn get_circuits(&self) -> Vec<CircuitInfo> {
654        self.circuits
655            .iter()
656            .map(|entry| entry.value().clone())
657            .collect()
658    }
659
660    /// Get all hidden services
661    pub fn get_hidden_services(&self) -> HashMap<OnionAddress, HiddenServiceConfig> {
662        self.hidden_services
663            .iter()
664            .map(|entry| (entry.key().clone(), entry.value().clone()))
665            .collect()
666    }
667
668    /// Get statistics
669    pub fn stats(&self) -> TorStats {
670        self.stats.read().clone()
671    }
672
673    /// Get configuration
674    pub fn config(&self) -> &TorConfig {
675        &self.config
676    }
677
678    /// Validate an onion address
679    pub fn validate_onion_address(address: &str) -> bool {
680        // v3 onion address: 56 characters + .onion
681        // v2 onion address: 16 characters + .onion (deprecated)
682
683        if !address.ends_with(".onion") {
684            return false;
685        }
686
687        let name = address
688            .strip_suffix(".onion")
689            .expect("just confirmed ends_with('.onion')");
690
691        // Base32 character set: a-z, 2-7
692        let is_valid_base32 = |c: char| c.is_ascii_lowercase() || ('2'..='7').contains(&c);
693
694        // v3: 56 characters (base32 encoded)
695        if name.len() == 56 {
696            return name.chars().all(is_valid_base32);
697        }
698
699        // v2: 16 characters (base32 encoded, deprecated)
700        if name.len() == 16 {
701            return name.chars().all(is_valid_base32);
702        }
703
704        false
705    }
706}
707
708#[cfg(test)]
709mod tests {
710    use super::*;
711
712    #[tokio::test]
713    async fn test_manager_creation() {
714        let config = TorConfig::default();
715        let manager = TorManager::new(config)
716            .await
717            .expect("test: TorManager::new should succeed");
718
719        assert!(!manager.is_running());
720        assert_eq!(manager.stats().circuits_created, 0);
721    }
722
723    #[tokio::test]
724    async fn test_start_stop() {
725        let config = TorConfig::default();
726        let mut manager = TorManager::new(config)
727            .await
728            .expect("test: TorManager::new should succeed");
729
730        assert!(!manager.is_running());
731
732        manager.start().await.expect("test: start should succeed");
733        assert!(manager.is_running());
734
735        manager.stop().await.expect("test: stop should succeed");
736        assert!(!manager.is_running());
737    }
738
739    #[tokio::test]
740    async fn test_create_circuit() {
741        let config = TorConfig::default();
742        let mut manager = TorManager::new(config)
743            .await
744            .expect("test: TorManager::new should succeed");
745
746        manager.start().await.expect("test: start should succeed");
747
748        let circuit_id = manager
749            .create_circuit()
750            .await
751            .expect("test: create_circuit should succeed");
752        assert_eq!(circuit_id, 0);
753
754        let circuit = manager
755            .get_circuit(circuit_id)
756            .expect("test: circuit should exist after creation");
757        assert_eq!(circuit.state, CircuitState::Ready);
758        assert_eq!(circuit.hops.len(), 3);
759
760        let stats = manager.stats();
761        assert_eq!(stats.circuits_created, 1);
762        assert_eq!(stats.active_circuits, 1);
763    }
764
765    #[tokio::test]
766    async fn test_max_circuits_limit() {
767        let config = TorConfig {
768            max_circuits: 2,
769            circuit_timeout: Duration::from_millis(100),
770            ..Default::default()
771        };
772        let mut manager = TorManager::new(config)
773            .await
774            .expect("test: TorManager::new should succeed");
775
776        manager.start().await.expect("test: start should succeed");
777
778        // Create 2 circuits (should succeed)
779        let circuit1 = manager
780            .create_circuit()
781            .await
782            .expect("test: create first circuit should succeed");
783        let _circuit2 = manager
784            .create_circuit()
785            .await
786            .expect("test: create second circuit should succeed");
787
788        // Close first circuit's streams to make it eligible for cleanup
789        if let Some(mut circuit) = manager.circuits.get_mut(&circuit1) {
790            circuit.stream_count = 0;
791        }
792
793        // Wait for timeout
794        tokio::time::sleep(Duration::from_millis(150)).await;
795
796        // Try to create 3rd circuit (should succeed after cleanup)
797        let result = manager.create_circuit().await;
798        assert!(result.is_ok());
799    }
800
801    #[tokio::test]
802    async fn test_connect() {
803        let config = TorConfig::default();
804        let mut manager = TorManager::new(config)
805            .await
806            .expect("test: TorManager::new should succeed");
807
808        manager.start().await.expect("test: start should succeed");
809
810        let stream_id = manager
811            .connect("example.onion:8080")
812            .await
813            .expect("test: connect should succeed");
814        assert_eq!(stream_id, 0);
815
816        let stats = manager.stats();
817        assert_eq!(stats.streams_created, 1);
818        assert_eq!(stats.active_streams, 1);
819    }
820
821    #[tokio::test]
822    async fn test_stream_isolation() {
823        let config = TorConfig {
824            stream_isolation: true,
825            ..Default::default()
826        };
827        let mut manager = TorManager::new(config)
828            .await
829            .expect("test: TorManager::new should succeed");
830
831        manager.start().await.expect("test: start should succeed");
832
833        // Two streams should use different circuits with stream isolation
834        manager
835            .connect("example1.onion:8080")
836            .await
837            .expect("test: connect to example1 should succeed");
838        manager
839            .connect("example2.onion:8080")
840            .await
841            .expect("test: connect to example2 should succeed");
842
843        let stats = manager.stats();
844        assert_eq!(stats.circuits_created, 2);
845        assert_eq!(stats.streams_created, 2);
846    }
847
848    #[tokio::test]
849    async fn test_hidden_service() {
850        let config = TorConfig::default();
851        let mut manager = TorManager::new(config)
852            .await
853            .expect("test: TorManager::new should succeed");
854
855        manager.start().await.expect("test: start should succeed");
856
857        let hs_config = HiddenServiceConfig::default();
858        let onion_addr = manager
859            .create_hidden_service(hs_config)
860            .await
861            .expect("test: create_hidden_service should succeed");
862
863        assert!(onion_addr.ends_with(".onion"));
864        assert_eq!(onion_addr.len(), 62); // 56 chars + ".onion" (6 chars)
865
866        let stats = manager.stats();
867        assert_eq!(stats.hidden_services, 1);
868    }
869
870    #[tokio::test]
871    async fn test_remove_hidden_service() {
872        let config = TorConfig::default();
873        let mut manager = TorManager::new(config)
874            .await
875            .expect("test: TorManager::new should succeed");
876
877        manager.start().await.expect("test: start should succeed");
878
879        let hs_config = HiddenServiceConfig::default();
880        let onion_addr = manager
881            .create_hidden_service(hs_config)
882            .await
883            .expect("test: create_hidden_service should succeed");
884
885        manager
886            .remove_hidden_service(&onion_addr)
887            .await
888            .expect("test: remove_hidden_service should succeed");
889
890        let stats = manager.stats();
891        assert_eq!(stats.hidden_services, 0);
892    }
893
894    #[tokio::test]
895    async fn test_close_stream() {
896        let config = TorConfig::default();
897        let mut manager = TorManager::new(config)
898            .await
899            .expect("test: TorManager::new should succeed");
900
901        manager.start().await.expect("test: start should succeed");
902
903        let stream_id = manager
904            .connect("example.onion:8080")
905            .await
906            .expect("test: connect should succeed");
907        manager
908            .close_stream(stream_id)
909            .expect("test: close_stream should succeed");
910
911        let stats = manager.stats();
912        assert_eq!(stats.active_streams, 0);
913    }
914
915    #[tokio::test]
916    async fn test_config_presets() {
917        let high_privacy = TorConfig::high_privacy();
918        assert!(high_privacy.stream_isolation);
919        assert_eq!(high_privacy.max_circuits, 5);
920
921        let high_performance = TorConfig::high_performance();
922        assert!(!high_performance.stream_isolation);
923        assert_eq!(high_performance.max_circuits, 20);
924
925        let censorship = TorConfig::censorship_resistant();
926        assert!(censorship.use_bridges);
927        assert!(!censorship.bridges.is_empty());
928    }
929
930    #[test]
931    fn test_validate_onion_address() {
932        // Valid v3 onion address (56 characters, base32: a-z, 2-7)
933        let v3_addr = "abcdefghijklmnopqrstuvabcdefghijklmnopqrstuvabcdefghijkl.onion";
934        assert!(TorManager::validate_onion_address(v3_addr));
935
936        // Valid v2 onion address (16 characters, base32: a-z, 2-7)
937        let v2_addr = "abcdefghijklmnop.onion";
938        assert!(TorManager::validate_onion_address(v2_addr));
939
940        // Invalid addresses
941        assert!(!TorManager::validate_onion_address("invalid"));
942        assert!(!TorManager::validate_onion_address("example.com"));
943        assert!(!TorManager::validate_onion_address("abc.onion")); // Too short
944
945        // Invalid base32 characters (contains 8, 9, x, y, z)
946        assert!(!TorManager::validate_onion_address(
947            "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuvw.onion"
948        ));
949    }
950
951    #[tokio::test]
952    async fn test_circuit_cleanup() {
953        let config = TorConfig {
954            circuit_timeout: Duration::from_millis(100),
955            ..Default::default()
956        };
957        let mut manager = TorManager::new(config)
958            .await
959            .expect("test: TorManager::new should succeed");
960
961        manager.start().await.expect("test: start should succeed");
962
963        // Create a circuit
964        let _circuit_id = manager
965            .create_circuit()
966            .await
967            .expect("test: create_circuit should succeed");
968
969        // Wait for timeout
970        tokio::time::sleep(Duration::from_millis(150)).await;
971
972        // Cleanup should remove the old circuit
973        manager.cleanup_circuits();
974
975        // Circuit should still exist if we just created it
976        // But it should be cleaned up if it's old and unused
977    }
978
979    #[tokio::test]
980    async fn test_not_running_errors() {
981        let config = TorConfig::default();
982        let manager = TorManager::new(config)
983            .await
984            .expect("test: TorManager::new should succeed");
985
986        // These should fail when not running
987        assert!(manager.create_circuit().await.is_err());
988        assert!(manager.connect("example.onion:8080").await.is_err());
989
990        let hs_config = HiddenServiceConfig::default();
991        assert!(manager.create_hidden_service(hs_config).await.is_err());
992    }
993}