stem_rs/
controller.rs

1//! High-level controller API for Tor control protocol interaction.
2//!
3//! This module provides the primary interface for interacting with Tor's control
4//! protocol. The [`Controller`] type wraps a [`ControlSocket`](crate::socket::ControlSocket)
5//! and provides high-level methods for common operations like authentication,
6//! circuit management, stream handling, and event subscription.
7//!
8//! # Overview
9//!
10//! The Controller is the main entry point for most stem-rs users. It handles:
11//!
12//! - **Connection Management**: Connect via TCP port or Unix domain socket
13//! - **Authentication**: Automatic method detection and credential handling
14//! - **Information Queries**: GETINFO commands for version, PID, circuit status, etc.
15//! - **Configuration**: GETCONF/SETCONF/RESETCONF for Tor configuration
16//! - **Circuit Control**: Create, extend, and close circuits
17//! - **Stream Control**: Attach and close streams
18//! - **Event Handling**: Subscribe to and receive asynchronous events
19//! - **Hidden Services**: Create and manage ephemeral hidden services
20//! - **Address Mapping**: Map addresses for custom routing
21//!
22//! # Conceptual Role
23//!
24//! The Controller sits between your application and Tor's control socket:
25//!
26//! ```text
27//! ┌─────────────┐     ┌────────────┐     ┌─────────────┐
28//! │ Application │ ──▶│ Controller │ ──▶│ Tor Process │
29//! └─────────────┘     └────────────┘     └─────────────┘
30//!                           │
31//!                     Handles:
32//!                     • Protocol formatting
33//!                     • Response parsing
34//!                     • Event buffering
35//!                     • Error handling
36//! ```
37//!
38//! # What This Module Does NOT Do
39//!
40//! - **Direct relay communication**: Use [`client::Relay`](crate::client::Relay) for ORPort connections
41//! - **Descriptor parsing**: Use the [`descriptor`](crate::descriptor) module
42//! - **Exit policy evaluation**: Use [`ExitPolicy`](crate::exit_policy::ExitPolicy)
43//!
44//! # Thread Safety
45//!
46//! [`Controller`] is `Send` but not `Sync`. The controller maintains internal
47//! state (socket, event buffer) that requires exclusive access. For concurrent
48//! access from multiple tasks, wrap in `Arc<Mutex<Controller>>`:
49//!
50//! ```rust,no_run
51//! use std::sync::Arc;
52//! use tokio::sync::Mutex;
53//! use stem_rs::controller::Controller;
54//!
55//! # async fn example() -> Result<(), stem_rs::Error> {
56//! let controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
57//! let shared = Arc::new(Mutex::new(controller));
58//!
59//! // Clone Arc for each task
60//! let c1 = shared.clone();
61//! tokio::spawn(async move {
62//!     let mut ctrl = c1.lock().await;
63//!     // Use controller...
64//! });
65//! # Ok(())
66//! # }
67//! ```
68//!
69//! # Example
70//!
71//! Basic usage pattern:
72//!
73//! ```rust,no_run
74//! use stem_rs::controller::Controller;
75//! use stem_rs::Signal;
76//!
77//! # async fn example() -> Result<(), stem_rs::Error> {
78//! // Connect to Tor's control port
79//! let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
80//!
81//! // Authenticate (auto-detects method)
82//! controller.authenticate(None).await?;
83//!
84//! // Query information
85//! let version = controller.get_version().await?;
86//! println!("Connected to Tor {}", version);
87//!
88//! // Get active circuits
89//! let circuits = controller.get_circuits().await?;
90//! for circuit in circuits {
91//!     println!("Circuit {}: {:?}", circuit.id, circuit.status);
92//! }
93//!
94//! // Request new identity
95//! controller.signal(Signal::Newnym).await?;
96//! # Ok(())
97//! # }
98//! ```
99//!
100//! # Security Considerations
101//!
102//! - Passwords are not stored after authentication
103//! - Cookie files are read with minimal permissions
104//! - SAFECOOKIE authentication uses secure random nonces
105//! - Input is validated to prevent protocol injection attacks
106//!
107//! # See Also
108//!
109//! - [`socket`](crate::socket): Low-level socket communication
110//! - [`auth`](crate::auth): Authentication implementation details
111//! - [`events`](crate::events): Event types for subscription
112//! - Python Stem's `Controller` class for equivalent functionality
113
114use std::collections::HashMap;
115use std::net::SocketAddr;
116use std::path::Path;
117
118use crate::auth;
119use crate::events::ParsedEvent;
120use crate::protocol::ControlLine;
121use crate::socket::{ControlMessage, ControlSocket};
122use crate::version::Version;
123use crate::{CircStatus, Error, EventType, Signal, StreamStatus};
124
125/// A unique identifier for a Tor circuit.
126///
127/// Circuit IDs are assigned by Tor when circuits are created and are used
128/// to reference specific circuits in control protocol commands. The ID is
129/// a string representation of a numeric identifier.
130///
131/// # Invariants
132///
133/// - Circuit IDs are unique within a Tor session
134/// - IDs are assigned sequentially by Tor
135/// - An ID remains valid until the circuit is closed
136///
137/// # Example
138///
139/// ```rust
140/// use stem_rs::controller::CircuitId;
141///
142/// let id = CircuitId::new("42");
143/// assert_eq!(id.to_string(), "42");
144///
145/// // CircuitIds can be compared for equality
146/// let id2 = CircuitId::new("42");
147/// assert_eq!(id, id2);
148/// ```
149///
150/// # See Also
151///
152/// - [`Controller::get_circuits`]: Retrieve active circuits
153/// - [`Controller::new_circuit`]: Create a new circuit
154/// - [`Controller::close_circuit`]: Close a circuit by ID
155#[derive(Debug, Clone, PartialEq, Eq, Hash)]
156pub struct CircuitId(pub String);
157
158impl CircuitId {
159    /// Creates a new circuit ID from any string-like value.
160    ///
161    /// # Arguments
162    ///
163    /// * `id` - The circuit identifier, typically a numeric string
164    ///
165    /// # Example
166    ///
167    /// ```rust
168    /// use stem_rs::controller::CircuitId;
169    ///
170    /// let id = CircuitId::new("123");
171    /// let id_from_string = CircuitId::new(String::from("123"));
172    /// assert_eq!(id, id_from_string);
173    /// ```
174    pub fn new(id: impl Into<String>) -> Self {
175        Self(id.into())
176    }
177}
178
179impl std::fmt::Display for CircuitId {
180    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181        write!(f, "{}", self.0)
182    }
183}
184
185/// A unique identifier for a Tor stream.
186///
187/// Stream IDs are assigned by Tor when streams are created and are used
188/// to reference specific streams in control protocol commands. Streams
189/// represent individual TCP connections being routed through Tor circuits.
190///
191/// # Invariants
192///
193/// - Stream IDs are unique within a Tor session
194/// - IDs are assigned sequentially by Tor
195/// - An ID remains valid until the stream is closed
196///
197/// # Example
198///
199/// ```rust
200/// use stem_rs::controller::StreamId;
201///
202/// let id = StreamId::new("99");
203/// assert_eq!(id.to_string(), "99");
204///
205/// // StreamIds can be compared for equality
206/// let id2 = StreamId::new("99");
207/// assert_eq!(id, id2);
208/// ```
209///
210/// # See Also
211///
212/// - [`Controller::get_streams`]: Retrieve active streams
213/// - [`Controller::attach_stream`]: Attach a stream to a circuit
214/// - [`Controller::close_stream`]: Close a stream by ID
215#[derive(Debug, Clone, PartialEq, Eq, Hash)]
216pub struct StreamId(pub String);
217
218impl StreamId {
219    /// Creates a new stream ID from any string-like value.
220    ///
221    /// # Arguments
222    ///
223    /// * `id` - The stream identifier, typically a numeric string
224    ///
225    /// # Example
226    ///
227    /// ```rust
228    /// use stem_rs::controller::StreamId;
229    ///
230    /// let id = StreamId::new("456");
231    /// let id_from_string = StreamId::new(String::from("456"));
232    /// assert_eq!(id, id_from_string);
233    /// ```
234    pub fn new(id: impl Into<String>) -> Self {
235        Self(id.into())
236    }
237}
238
239impl std::fmt::Display for StreamId {
240    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241        write!(f, "{}", self.0)
242    }
243}
244
245/// Information about a relay in a circuit path.
246///
247/// Each hop in a Tor circuit is represented by a `RelayInfo` containing
248/// the relay's fingerprint and optionally its nickname. The fingerprint
249/// is a 40-character hexadecimal string representing the SHA-1 hash of
250/// the relay's identity key.
251///
252/// # Fields
253///
254/// - `fingerprint`: The relay's identity fingerprint (40 hex characters)
255/// - `nickname`: The relay's optional human-readable nickname
256///
257/// # Example
258///
259/// ```rust
260/// use stem_rs::controller::RelayInfo;
261///
262/// let relay = RelayInfo {
263///     fingerprint: "9695DFC35FFEB861329B9F1AB04C46397020CE31".to_string(),
264///     nickname: Some("MyRelay".to_string()),
265/// };
266///
267/// println!("Relay: {} ({:?})", relay.fingerprint, relay.nickname);
268/// ```
269///
270/// # See Also
271///
272/// - [`Circuit`]: Contains a path of `RelayInfo` entries
273/// - [`util::is_valid_fingerprint`](crate::util::is_valid_fingerprint): Validate fingerprint format
274#[derive(Debug, Clone)]
275pub struct RelayInfo {
276    /// The relay's identity fingerprint (40 hexadecimal characters).
277    ///
278    /// This is the SHA-1 hash of the relay's identity key, used to uniquely
279    /// identify relays across the Tor network.
280    pub fingerprint: String,
281
282    /// The relay's optional human-readable nickname.
283    ///
284    /// Nicknames are chosen by relay operators and are not guaranteed to be
285    /// unique. May be `None` if the nickname was not provided in the circuit
286    /// status response.
287    pub nickname: Option<String>,
288}
289
290/// Information about an active Tor circuit.
291///
292/// A circuit is a path through the Tor network consisting of multiple
293/// relay hops. Circuits are used to route traffic anonymously by encrypting
294/// data in layers that are peeled off at each hop.
295///
296/// # Circuit Lifecycle
297///
298/// Circuits progress through several states:
299///
300/// 1. **Launched**: Circuit creation has begun
301/// 2. **Extended**: Circuit is being extended to additional hops
302/// 3. **Built**: Circuit is fully constructed and ready for use
303/// 4. **Failed**: Circuit construction failed
304/// 5. **Closed**: Circuit has been closed
305///
306/// # Fields
307///
308/// - `id`: Unique identifier for this circuit
309/// - `status`: Current state of the circuit
310/// - `path`: Ordered list of relays in the circuit (guard → middle → exit)
311///
312/// # Example
313///
314/// ```rust,no_run
315/// use stem_rs::controller::Controller;
316/// use stem_rs::CircStatus;
317///
318/// # async fn example() -> Result<(), stem_rs::Error> {
319/// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
320/// controller.authenticate(None).await?;
321///
322/// for circuit in controller.get_circuits().await? {
323///     if circuit.status == CircStatus::Built {
324///         println!("Circuit {} has {} hops:", circuit.id, circuit.path.len());
325///         for (i, relay) in circuit.path.iter().enumerate() {
326///             println!("  Hop {}: {} ({:?})", i + 1, relay.fingerprint, relay.nickname);
327///         }
328///     }
329/// }
330/// # Ok(())
331/// # }
332/// ```
333///
334/// # See Also
335///
336/// - [`Controller::get_circuits`]: Retrieve all active circuits
337/// - [`Controller::new_circuit`]: Create a new circuit
338/// - [`CircStatus`](crate::CircStatus): Circuit status enumeration
339#[derive(Debug, Clone)]
340pub struct Circuit {
341    /// Unique identifier for this circuit.
342    pub id: CircuitId,
343
344    /// Current status of the circuit.
345    ///
346    /// See [`CircStatus`](crate::CircStatus) for possible values.
347    pub status: CircStatus,
348
349    /// Ordered list of relays in the circuit path.
350    ///
351    /// The first relay is the guard (entry) node, and the last relay is
352    /// typically the exit node. The path may be empty for newly launched
353    /// circuits that haven't yet established any hops.
354    pub path: Vec<RelayInfo>,
355}
356
357/// Information about an active Tor stream.
358///
359/// A stream represents a single TCP connection being routed through a Tor
360/// circuit. Streams are created when applications connect through Tor's
361/// SOCKS proxy and are attached to circuits for routing.
362///
363/// # Stream Lifecycle
364///
365/// Streams progress through several states:
366///
367/// 1. **New**: Stream created, awaiting circuit attachment
368/// 2. **SentConnect**: CONNECT command sent to exit relay
369/// 3. **Succeeded**: Connection established successfully
370/// 4. **Failed**: Connection attempt failed
371/// 5. **Closed**: Stream has been closed
372///
373/// # Fields
374///
375/// - `id`: Unique identifier for this stream
376/// - `status`: Current state of the stream
377/// - `circuit_id`: The circuit this stream is attached to (if any)
378/// - `target_host`: Destination hostname or IP address
379/// - `target_port`: Destination port number
380///
381/// # Example
382///
383/// ```rust,no_run
384/// use stem_rs::controller::Controller;
385/// use stem_rs::StreamStatus;
386///
387/// # async fn example() -> Result<(), stem_rs::Error> {
388/// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
389/// controller.authenticate(None).await?;
390///
391/// for stream in controller.get_streams().await? {
392///     println!("Stream {} -> {}:{} ({:?})",
393///         stream.id,
394///         stream.target_host,
395///         stream.target_port,
396///         stream.status
397///     );
398///     if let Some(ref circuit_id) = stream.circuit_id {
399///         println!("  Attached to circuit {}", circuit_id);
400///     }
401/// }
402/// # Ok(())
403/// # }
404/// ```
405///
406/// # See Also
407///
408/// - [`Controller::get_streams`]: Retrieve all active streams
409/// - [`Controller::attach_stream`]: Attach a stream to a circuit
410/// - [`StreamStatus`](crate::StreamStatus): Stream status enumeration
411#[derive(Debug, Clone)]
412pub struct Stream {
413    /// Unique identifier for this stream.
414    pub id: StreamId,
415
416    /// Current status of the stream.
417    ///
418    /// See [`StreamStatus`](crate::StreamStatus) for possible values.
419    pub status: StreamStatus,
420
421    /// The circuit this stream is attached to, if any.
422    ///
423    /// Streams in the `New` or `Detached` state may not be attached to
424    /// any circuit. Once attached, this field contains the circuit ID.
425    pub circuit_id: Option<CircuitId>,
426
427    /// Destination hostname or IP address.
428    ///
429    /// This is the target the stream is connecting to through Tor.
430    pub target_host: String,
431
432    /// Destination port number.
433    ///
434    /// The TCP port on the target host. May be 0 if not specified.
435    pub target_port: u16,
436}
437
438/// A high-level interface for interacting with Tor's control protocol.
439///
440/// The `Controller` provides the primary API for controlling a Tor process.
441/// It wraps a [`ControlSocket`](crate::socket::ControlSocket) and provides
442/// typed methods for common operations like authentication, circuit management,
443/// and event subscription.
444///
445/// # Conceptual Role
446///
447/// The Controller is the main entry point for most stem-rs users. It handles:
448///
449/// - Protocol message formatting and parsing
450/// - Response validation and error handling
451/// - Asynchronous event buffering
452/// - Connection lifecycle management
453///
454/// # What This Type Does NOT Do
455///
456/// - Direct relay communication (use [`client::Relay`](crate::client::Relay))
457/// - Descriptor parsing (use [`descriptor`](crate::descriptor) module)
458/// - Exit policy evaluation (use [`ExitPolicy`](crate::exit_policy::ExitPolicy))
459///
460/// # Invariants
461///
462/// - The underlying socket connection is valid while the Controller exists
463/// - After successful authentication, the controller is ready for commands
464/// - Events received during command execution are buffered for later retrieval
465///
466/// # Thread Safety
467///
468/// `Controller` is `Send` but not `Sync`. For concurrent access from multiple
469/// tasks, wrap in `Arc<Mutex<Controller>>`:
470///
471/// ```rust,no_run
472/// use std::sync::Arc;
473/// use tokio::sync::Mutex;
474/// use stem_rs::controller::Controller;
475///
476/// # async fn example() -> Result<(), stem_rs::Error> {
477/// let controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
478/// let shared = Arc::new(Mutex::new(controller));
479///
480/// // Clone Arc for each task
481/// let c1 = shared.clone();
482/// tokio::spawn(async move {
483///     let mut ctrl = c1.lock().await;
484///     // Use controller...
485/// });
486/// # Ok(())
487/// # }
488/// ```
489///
490/// # Example
491///
492/// ```rust,no_run
493/// use stem_rs::controller::Controller;
494/// use stem_rs::Signal;
495///
496/// # async fn example() -> Result<(), stem_rs::Error> {
497/// // Connect and authenticate
498/// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
499/// controller.authenticate(Some("my_password")).await?;
500///
501/// // Query information
502/// let version = controller.get_version().await?;
503/// let circuits = controller.get_circuits().await?;
504///
505/// // Send signal
506/// controller.signal(Signal::Newnym).await?;
507/// # Ok(())
508/// # }
509/// ```
510///
511/// # Security
512///
513/// - Passwords are not stored after authentication
514/// - Cookie files are read with minimal permissions
515/// - SAFECOOKIE uses secure random nonces
516///
517/// # See Also
518///
519/// - [`from_port`](Controller::from_port): Connect via TCP
520/// - [`from_socket_file`](Controller::from_socket_file): Connect via Unix socket
521/// - [`authenticate`](Controller::authenticate): Authenticate with Tor
522pub struct Controller {
523    /// The underlying control socket connection.
524    socket: ControlSocket,
525    /// Buffer for asynchronous events received during command execution.
526    event_buffer: Vec<ControlMessage>,
527}
528
529impl Controller {
530    /// Creates a new Controller connected to a TCP control port.
531    ///
532    /// Establishes a TCP connection to Tor's control port at the specified
533    /// address. The connection is unauthenticated; call [`authenticate`](Self::authenticate)
534    /// before issuing commands.
535    ///
536    /// # Arguments
537    ///
538    /// * `addr` - The socket address of Tor's control port (e.g., `127.0.0.1:9051`)
539    ///
540    /// # Errors
541    ///
542    /// Returns [`Error::Socket`](crate::Error::Socket) if:
543    /// - The connection is refused (Tor not running or port incorrect)
544    /// - Network is unreachable
545    /// - Connection times out
546    ///
547    /// # Example
548    ///
549    /// ```rust,no_run
550    /// use stem_rs::controller::Controller;
551    ///
552    /// # async fn example() -> Result<(), stem_rs::Error> {
553    /// // Connect to default control port
554    /// let controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
555    ///
556    /// // Connect to custom port
557    /// let controller = Controller::from_port("127.0.0.1:9151".parse()?).await?;
558    /// # Ok(())
559    /// # }
560    /// ```
561    ///
562    /// # See Also
563    ///
564    /// - [`from_socket_file`](Self::from_socket_file): Connect via Unix socket
565    /// - [`authenticate`](Self::authenticate): Authenticate after connecting
566    pub async fn from_port(addr: SocketAddr) -> Result<Self, Error> {
567        let socket = ControlSocket::connect_port(addr).await?;
568        Ok(Self {
569            socket,
570            event_buffer: Vec::new(),
571        })
572    }
573
574    /// Creates a new Controller connected to a Unix domain socket.
575    ///
576    /// Establishes a connection to Tor's control socket at the specified
577    /// file path. This is typically more secure than TCP as it doesn't
578    /// expose the control interface to the network.
579    ///
580    /// # Arguments
581    ///
582    /// * `path` - Path to Tor's control socket file (e.g., `/var/run/tor/control`)
583    ///
584    /// # Errors
585    ///
586    /// Returns [`Error::Socket`](crate::Error::Socket) if:
587    /// - The socket file doesn't exist
588    /// - Permission denied accessing the socket
589    /// - The socket is not a valid Unix domain socket
590    ///
591    /// # Example
592    ///
593    /// ```rust,no_run
594    /// use std::path::Path;
595    /// use stem_rs::controller::Controller;
596    ///
597    /// # async fn example() -> Result<(), stem_rs::Error> {
598    /// // Connect to Tor's Unix control socket
599    /// let controller = Controller::from_socket_file(
600    ///     Path::new("/var/run/tor/control")
601    /// ).await?;
602    /// # Ok(())
603    /// # }
604    /// ```
605    ///
606    /// # Platform Support
607    ///
608    /// Unix domain sockets are only available on Unix-like systems (Linux, macOS, BSD).
609    /// On Windows, use [`from_port`](Self::from_port) instead.
610    ///
611    /// # See Also
612    ///
613    /// - [`from_port`](Self::from_port): Connect via TCP
614    /// - [`authenticate`](Self::authenticate): Authenticate after connecting
615    pub async fn from_socket_file(path: &Path) -> Result<Self, Error> {
616        let socket = ControlSocket::connect_unix(path).await?;
617        Ok(Self {
618            socket,
619            event_buffer: Vec::new(),
620        })
621    }
622
623    /// Receives a response, buffering any asynchronous events.
624    ///
625    /// This internal method reads responses from the socket, automatically
626    /// buffering any asynchronous events (status code 650) that arrive
627    /// while waiting for a command response.
628    async fn recv_response(&mut self) -> Result<ControlMessage, Error> {
629        loop {
630            let response = self.socket.recv().await?;
631            if response.status_code == 650 {
632                self.event_buffer.push(response);
633            } else {
634                return Ok(response);
635            }
636        }
637    }
638
639    /// Authenticates with the Tor control interface.
640    ///
641    /// Attempts authentication using the best available method. If `password`
642    /// is provided, PASSWORD authentication is attempted. Otherwise, the method
643    /// is auto-detected from PROTOCOLINFO.
644    ///
645    /// # Authentication Methods
646    ///
647    /// Methods are tried in this order:
648    /// 1. **NONE** - If control port is open (no auth required)
649    /// 2. **SAFECOOKIE** - Preferred for local connections
650    /// 3. **COOKIE** - Fallback for older Tor versions
651    /// 4. **PASSWORD** - If password is provided
652    ///
653    /// # Arguments
654    ///
655    /// * `password` - Optional password for PASSWORD authentication
656    ///
657    /// # Preconditions
658    ///
659    /// - Socket must be connected (not closed)
660    /// - No prior successful authentication on this connection
661    ///
662    /// # Postconditions
663    ///
664    /// - On success: Controller is authenticated and ready for commands
665    /// - On failure: Connection state is undefined; reconnect recommended
666    ///
667    /// # Errors
668    ///
669    /// Returns [`Error::Authentication`](crate::Error::Authentication) with specific reason:
670    ///
671    /// - [`AuthError::NoMethods`](crate::AuthError::NoMethods) - No compatible auth methods available
672    /// - [`AuthError::IncorrectPassword`](crate::AuthError::IncorrectPassword) - PASSWORD auth failed
673    /// - [`AuthError::CookieUnreadable`](crate::AuthError::CookieUnreadable) - Cannot read cookie file
674    /// - [`AuthError::IncorrectCookie`](crate::AuthError::IncorrectCookie) - COOKIE auth failed
675    /// - [`AuthError::ChallengeFailed`](crate::AuthError::ChallengeFailed) - SAFECOOKIE challenge failed
676    ///
677    /// # Example
678    ///
679    /// ```rust,no_run
680    /// use stem_rs::controller::Controller;
681    ///
682    /// # async fn example() -> Result<(), stem_rs::Error> {
683    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
684    ///
685    /// // Auto-detect authentication method
686    /// controller.authenticate(None).await?;
687    ///
688    /// // Or use password authentication
689    /// controller.authenticate(Some("my_password")).await?;
690    /// # Ok(())
691    /// # }
692    /// ```
693    ///
694    /// # Security
695    ///
696    /// - Passwords are cleared from memory after use
697    /// - Cookie comparison uses constant-time algorithm
698    /// - SAFECOOKIE nonces are cryptographically random
699    ///
700    /// # See Also
701    ///
702    /// - [`auth`](crate::auth): Authentication implementation details
703    /// - [`AuthError`](crate::AuthError): Authentication error types
704    pub async fn authenticate(&mut self, password: Option<&str>) -> Result<(), Error> {
705        auth::authenticate(&mut self.socket, password).await
706    }
707
708    /// Queries Tor for information using the GETINFO command.
709    ///
710    /// GETINFO retrieves various pieces of information from Tor. The available
711    /// keys depend on Tor's version and configuration.
712    ///
713    /// # Arguments
714    ///
715    /// * `key` - The information key to query (e.g., "version", "circuit-status")
716    ///
717    /// # Common Keys
718    ///
719    /// | Key | Description |
720    /// |-----|-------------|
721    /// | `version` | Tor version string |
722    /// | `process/pid` | Tor process ID |
723    /// | `circuit-status` | Active circuit information |
724    /// | `stream-status` | Active stream information |
725    /// | `address` | Best guess at external IP address |
726    /// | `fingerprint` | Relay fingerprint (if running as relay) |
727    /// | `config-file` | Path to torrc file |
728    ///
729    /// # Errors
730    ///
731    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
732    /// - The key is unrecognized
733    /// - The information is not available
734    /// - Tor returns an error response
735    ///
736    /// # Example
737    ///
738    /// ```rust,no_run
739    /// use stem_rs::controller::Controller;
740    ///
741    /// # async fn example() -> Result<(), stem_rs::Error> {
742    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
743    /// controller.authenticate(None).await?;
744    ///
745    /// // Query Tor version
746    /// let version = controller.get_info("version").await?;
747    /// println!("Tor version: {}", version);
748    ///
749    /// // Query external IP address
750    /// let address = controller.get_info("address").await?;
751    /// println!("External IP: {}", address);
752    /// # Ok(())
753    /// # }
754    /// ```
755    ///
756    /// # See Also
757    ///
758    /// - [`get_version`](Self::get_version): Typed version query
759    /// - [`get_pid`](Self::get_pid): Typed PID query
760    pub async fn get_info(&mut self, key: &str) -> Result<String, Error> {
761        let command = format!("GETINFO {}", key);
762        self.socket.send(&command).await?;
763        let response = self.recv_response().await?;
764
765        if !response.is_ok() {
766            return Err(Error::OperationFailed {
767                code: response.status_code.to_string(),
768                message: response.content().to_string(),
769            });
770        }
771
772        for line in &response.lines {
773            if let Some(rest) = line.strip_prefix(&format!("{}=", key)) {
774                return Ok(rest.to_string());
775            }
776            if line.starts_with(&format!("{}\n", key)) {
777                return Ok(line
778                    .strip_prefix(&format!("{}\n", key))
779                    .unwrap_or("")
780                    .to_string());
781            }
782        }
783
784        Ok(response.content().to_string())
785    }
786
787    /// Retrieves the Tor version as a parsed [`Version`] object.
788    ///
789    /// This is a convenience wrapper around [`get_info("version")`](Self::get_info)
790    /// that parses the version string into a structured [`Version`] type.
791    ///
792    /// # Errors
793    ///
794    /// Returns an error if:
795    /// - The GETINFO command fails
796    /// - The version string cannot be parsed
797    ///
798    /// # Example
799    ///
800    /// ```rust,no_run
801    /// use stem_rs::controller::Controller;
802    ///
803    /// # async fn example() -> Result<(), stem_rs::Error> {
804    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
805    /// controller.authenticate(None).await?;
806    ///
807    /// let version = controller.get_version().await?;
808    /// println!("Tor version: {}", version);
809    ///
810    /// // Version supports comparison
811    /// // if version >= Version::parse("0.4.0.0")? { ... }
812    /// # Ok(())
813    /// # }
814    /// ```
815    ///
816    /// # See Also
817    ///
818    /// - [`Version`](crate::version::Version): Version type with comparison support
819    pub async fn get_version(&mut self) -> Result<Version, Error> {
820        let version_str = self.get_info("version").await?;
821        Version::parse(&version_str)
822    }
823
824    /// Retrieves the process ID of the Tor process.
825    ///
826    /// This is a convenience wrapper around [`get_info("process/pid")`](Self::get_info)
827    /// that parses the PID into a `u32`.
828    ///
829    /// # Errors
830    ///
831    /// Returns an error if:
832    /// - The GETINFO command fails
833    /// - The PID string cannot be parsed as a number
834    ///
835    /// # Example
836    ///
837    /// ```rust,no_run
838    /// use stem_rs::controller::Controller;
839    ///
840    /// # async fn example() -> Result<(), stem_rs::Error> {
841    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
842    /// controller.authenticate(None).await?;
843    ///
844    /// let pid = controller.get_pid().await?;
845    /// println!("Tor PID: {}", pid);
846    /// # Ok(())
847    /// # }
848    /// ```
849    pub async fn get_pid(&mut self) -> Result<u32, Error> {
850        let pid_str = self.get_info("process/pid").await?;
851        pid_str.parse().map_err(|_| Error::Parse {
852            location: "pid".to_string(),
853            reason: format!("invalid pid: {}", pid_str),
854        })
855    }
856
857    /// Retrieves the value(s) of a Tor configuration option.
858    ///
859    /// Uses the GETCONF command to query Tor's current configuration.
860    /// Some options can have multiple values, so this returns a `Vec<String>`.
861    ///
862    /// # Arguments
863    ///
864    /// * `key` - The configuration option name (e.g., "SocksPort", "ExitPolicy")
865    ///
866    /// # Errors
867    ///
868    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
869    /// - The configuration option is unrecognized
870    /// - Tor returns an error response
871    ///
872    /// # Example
873    ///
874    /// ```rust,no_run
875    /// use stem_rs::controller::Controller;
876    ///
877    /// # async fn example() -> Result<(), stem_rs::Error> {
878    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
879    /// controller.authenticate(None).await?;
880    ///
881    /// // Get SOCKS port configuration
882    /// let socks_ports = controller.get_conf("SocksPort").await?;
883    /// for port in socks_ports {
884    ///     println!("SOCKS port: {}", port);
885    /// }
886    /// # Ok(())
887    /// # }
888    /// ```
889    ///
890    /// # See Also
891    ///
892    /// - [`set_conf`](Self::set_conf): Set a configuration option
893    /// - [`reset_conf`](Self::reset_conf): Reset to default value
894    pub async fn get_conf(&mut self, key: &str) -> Result<Vec<String>, Error> {
895        let command = format!("GETCONF {}", key);
896        self.socket.send(&command).await?;
897        let response = self.recv_response().await?;
898
899        if !response.is_ok() {
900            return Err(Error::OperationFailed {
901                code: response.status_code.to_string(),
902                message: response.content().to_string(),
903            });
904        }
905
906        let mut values = Vec::new();
907        for line in &response.lines {
908            if let Some(rest) = line.strip_prefix(&format!("{}=", key)) {
909                values.push(rest.to_string());
910            } else if line
911                .to_lowercase()
912                .starts_with(&format!("{}=", key.to_lowercase()))
913            {
914                let eq_pos = line.find('=').unwrap_or(line.len());
915                values.push(line[eq_pos + 1..].to_string());
916            }
917        }
918
919        if values.is_empty() && !response.lines.is_empty() {
920            let first_line = &response.lines[0];
921            if let Some(eq_pos) = first_line.find('=') {
922                values.push(first_line[eq_pos + 1..].to_string());
923            }
924        }
925
926        Ok(values)
927    }
928
929    /// Sets a Tor configuration option.
930    ///
931    /// Uses the SETCONF command to change Tor's configuration at runtime.
932    /// The change takes effect immediately but is not persisted to the torrc
933    /// file unless you call `save_conf`.
934    ///
935    /// # Arguments
936    ///
937    /// * `key` - The configuration option name
938    /// * `value` - The new value for the option
939    ///
940    /// # Value Escaping
941    ///
942    /// Values containing spaces or quotes are automatically escaped. You don't
943    /// need to handle quoting yourself.
944    ///
945    /// # Errors
946    ///
947    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
948    /// - The configuration option is unrecognized
949    /// - The value is invalid for this option
950    /// - The option cannot be changed at runtime
951    /// - Tor returns an error response
952    ///
953    /// # Example
954    ///
955    /// ```rust,no_run
956    /// use stem_rs::controller::Controller;
957    ///
958    /// # async fn example() -> Result<(), stem_rs::Error> {
959    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
960    /// controller.authenticate(None).await?;
961    ///
962    /// // Change bandwidth rate
963    /// controller.set_conf("BandwidthRate", "1 MB").await?;
964    ///
965    /// // Enable a feature
966    /// controller.set_conf("SafeLogging", "1").await?;
967    /// # Ok(())
968    /// # }
969    /// ```
970    ///
971    /// # See Also
972    ///
973    /// - [`get_conf`](Self::get_conf): Get current configuration
974    /// - [`reset_conf`](Self::reset_conf): Reset to default value
975    pub async fn set_conf(&mut self, key: &str, value: &str) -> Result<(), Error> {
976        let command = if value.contains(' ') || value.contains('"') {
977            format!(
978                "SETCONF {}=\"{}\"",
979                key,
980                value.replace('\\', "\\\\").replace('"', "\\\"")
981            )
982        } else {
983            format!("SETCONF {}={}", key, value)
984        };
985        self.socket.send(&command).await?;
986        let response = self.recv_response().await?;
987
988        if response.is_ok() {
989            Ok(())
990        } else {
991            Err(Error::OperationFailed {
992                code: response.status_code.to_string(),
993                message: response.content().to_string(),
994            })
995        }
996    }
997
998    /// Resets a Tor configuration option to its default value.
999    ///
1000    /// Uses the RESETCONF command to restore a configuration option to its
1001    /// default value as if it were not set in the torrc file.
1002    ///
1003    /// # Arguments
1004    ///
1005    /// * `key` - The configuration option name to reset
1006    ///
1007    /// # Errors
1008    ///
1009    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
1010    /// - The configuration option is unrecognized
1011    /// - The option cannot be reset at runtime
1012    /// - Tor returns an error response
1013    ///
1014    /// # Example
1015    ///
1016    /// ```rust,no_run
1017    /// use stem_rs::controller::Controller;
1018    ///
1019    /// # async fn example() -> Result<(), stem_rs::Error> {
1020    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1021    /// controller.authenticate(None).await?;
1022    ///
1023    /// // Reset bandwidth rate to default
1024    /// controller.reset_conf("BandwidthRate").await?;
1025    /// # Ok(())
1026    /// # }
1027    /// ```
1028    ///
1029    /// # See Also
1030    ///
1031    /// - [`get_conf`](Self::get_conf): Get current configuration
1032    /// - [`set_conf`](Self::set_conf): Set a configuration option
1033    pub async fn reset_conf(&mut self, key: &str) -> Result<(), Error> {
1034        let command = format!("RESETCONF {}", key);
1035        self.socket.send(&command).await?;
1036        let response = self.recv_response().await?;
1037
1038        if response.is_ok() {
1039            Ok(())
1040        } else {
1041            Err(Error::OperationFailed {
1042                code: response.status_code.to_string(),
1043                message: response.content().to_string(),
1044            })
1045        }
1046    }
1047
1048    /// Sends a signal to the Tor process.
1049    ///
1050    /// Signals control various aspects of Tor's behavior, from requesting
1051    /// new circuits to initiating shutdown.
1052    ///
1053    /// # Arguments
1054    ///
1055    /// * `signal` - The signal to send (see [`Signal`](crate::Signal))
1056    ///
1057    /// # Available Signals
1058    ///
1059    /// | Signal | Description |
1060    /// |--------|-------------|
1061    /// | [`Reload`](crate::Signal::Reload) | Reload configuration (SIGHUP) |
1062    /// | [`Shutdown`](crate::Signal::Shutdown) | Controlled shutdown |
1063    /// | [`Dump`](crate::Signal::Dump) | Write statistics to disk |
1064    /// | [`Debug`](crate::Signal::Debug) | Switch to debug logging |
1065    /// | [`Halt`](crate::Signal::Halt) | Immediate shutdown (SIGTERM) |
1066    /// | [`Newnym`](crate::Signal::Newnym) | Request new circuits |
1067    /// | [`ClearDnsCache`](crate::Signal::ClearDnsCache) | Clear DNS cache |
1068    /// | [`Heartbeat`](crate::Signal::Heartbeat) | Trigger heartbeat log |
1069    /// | [`Active`](crate::Signal::Active) | Wake from dormant mode |
1070    /// | [`Dormant`](crate::Signal::Dormant) | Enter dormant mode |
1071    ///
1072    /// # Errors
1073    ///
1074    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
1075    /// - The signal is not recognized
1076    /// - The signal cannot be sent (e.g., rate-limited NEWNYM)
1077    /// - Tor returns an error response
1078    ///
1079    /// # Example
1080    ///
1081    /// ```rust,no_run
1082    /// use stem_rs::controller::Controller;
1083    /// use stem_rs::Signal;
1084    ///
1085    /// # async fn example() -> Result<(), stem_rs::Error> {
1086    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1087    /// controller.authenticate(None).await?;
1088    ///
1089    /// // Request new identity (new circuits)
1090    /// controller.signal(Signal::Newnym).await?;
1091    ///
1092    /// // Reload configuration
1093    /// controller.signal(Signal::Reload).await?;
1094    ///
1095    /// // Clear DNS cache
1096    /// controller.signal(Signal::ClearDnsCache).await?;
1097    /// # Ok(())
1098    /// # }
1099    /// ```
1100    ///
1101    /// # Rate Limiting
1102    ///
1103    /// The `Newnym` signal is rate-limited by Tor to prevent abuse. If called
1104    /// too frequently, Tor may delay the signal or return an error.
1105    ///
1106    /// # See Also
1107    ///
1108    /// - [`Signal`](crate::Signal): Signal enumeration
1109    pub async fn signal(&mut self, signal: Signal) -> Result<(), Error> {
1110        let command = format!("SIGNAL {}", signal);
1111        self.socket.send(&command).await?;
1112        let response = self.recv_response().await?;
1113
1114        if response.is_ok() {
1115            Ok(())
1116        } else {
1117            Err(Error::OperationFailed {
1118                code: response.status_code.to_string(),
1119                message: response.content().to_string(),
1120            })
1121        }
1122    }
1123
1124    /// Retrieves information about all active circuits.
1125    ///
1126    /// Returns a list of all circuits currently known to Tor, including
1127    /// their status and path information.
1128    ///
1129    /// # Errors
1130    ///
1131    /// Returns an error if:
1132    /// - The GETINFO command fails
1133    /// - The circuit status cannot be parsed
1134    ///
1135    /// # Example
1136    ///
1137    /// ```rust,no_run
1138    /// use stem_rs::controller::Controller;
1139    /// use stem_rs::CircStatus;
1140    ///
1141    /// # async fn example() -> Result<(), stem_rs::Error> {
1142    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1143    /// controller.authenticate(None).await?;
1144    ///
1145    /// let circuits = controller.get_circuits().await?;
1146    /// for circuit in circuits {
1147    ///     if circuit.status == CircStatus::Built {
1148    ///         println!("Circuit {} is ready with {} hops",
1149    ///             circuit.id, circuit.path.len());
1150    ///     }
1151    /// }
1152    /// # Ok(())
1153    /// # }
1154    /// ```
1155    ///
1156    /// # See Also
1157    ///
1158    /// - [`Circuit`]: Circuit information structure
1159    /// - [`new_circuit`](Self::new_circuit): Create a new circuit
1160    /// - [`close_circuit`](Self::close_circuit): Close a circuit
1161    pub async fn get_circuits(&mut self) -> Result<Vec<Circuit>, Error> {
1162        let response_str = self.get_info("circuit-status").await?;
1163        parse_circuits(&response_str)
1164    }
1165
1166    /// Creates a new circuit, optionally with a specified path.
1167    ///
1168    /// If no path is specified, Tor will select relays automatically based
1169    /// on its path selection algorithm. If a path is provided, Tor will
1170    /// attempt to build a circuit through those specific relays.
1171    ///
1172    /// # Arguments
1173    ///
1174    /// * `path` - Optional list of relay fingerprints or nicknames for the circuit path
1175    ///
1176    /// # Path Specification
1177    ///
1178    /// Relays can be specified by:
1179    /// - Fingerprint: `$9695DFC35FFEB861329B9F1AB04C46397020CE31`
1180    /// - Nickname: `MyRelay`
1181    /// - Fingerprint with nickname: `$9695DFC35FFEB861329B9F1AB04C46397020CE31~MyRelay`
1182    ///
1183    /// # Errors
1184    ///
1185    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
1186    /// - A specified relay is unknown or unavailable
1187    /// - The path is invalid (e.g., too short)
1188    /// - Circuit creation fails
1189    ///
1190    /// # Example
1191    ///
1192    /// ```rust,no_run
1193    /// use stem_rs::controller::Controller;
1194    ///
1195    /// # async fn example() -> Result<(), stem_rs::Error> {
1196    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1197    /// controller.authenticate(None).await?;
1198    ///
1199    /// // Create circuit with automatic path selection
1200    /// let circuit_id = controller.new_circuit(None).await?;
1201    /// println!("Created circuit: {}", circuit_id);
1202    ///
1203    /// // Create circuit with specific path
1204    /// let path = &["$AAAA...", "$BBBB...", "$CCCC..."];
1205    /// let circuit_id = controller.new_circuit(Some(path)).await?;
1206    /// # Ok(())
1207    /// # }
1208    /// ```
1209    ///
1210    /// # See Also
1211    ///
1212    /// - [`extend_circuit`](Self::extend_circuit): Extend an existing circuit
1213    /// - [`close_circuit`](Self::close_circuit): Close a circuit
1214    /// - [`get_circuits`](Self::get_circuits): List active circuits
1215    pub async fn new_circuit(&mut self, path: Option<&[&str]>) -> Result<CircuitId, Error> {
1216        let command = match path {
1217            Some(relays) if !relays.is_empty() => {
1218                format!("EXTENDCIRCUIT 0 {}", relays.join(","))
1219            }
1220            _ => "EXTENDCIRCUIT 0".to_string(),
1221        };
1222        self.socket.send(&command).await?;
1223        let response = self.recv_response().await?;
1224
1225        if !response.is_ok() {
1226            return Err(Error::OperationFailed {
1227                code: response.status_code.to_string(),
1228                message: response.content().to_string(),
1229            });
1230        }
1231
1232        let content = response.content();
1233        let mut line = ControlLine::new(content);
1234        if line.is_next_mapping(Some("EXTENDED"), false) {
1235            let (_, circuit_id) = line.pop_mapping(false, false)?;
1236            return Ok(CircuitId::new(circuit_id));
1237        }
1238
1239        let circuit_id = line.pop(false, false)?;
1240        Ok(CircuitId::new(circuit_id))
1241    }
1242
1243    /// Extends an existing circuit by adding additional hops.
1244    ///
1245    /// Adds one or more relays to an existing circuit. The circuit must be
1246    /// in a state that allows extension (typically BUILT or EXTENDED).
1247    ///
1248    /// # Arguments
1249    ///
1250    /// * `id` - The circuit ID to extend
1251    /// * `path` - List of relay fingerprints or nicknames to add
1252    ///
1253    /// # Errors
1254    ///
1255    /// Returns [`Error::InvalidArguments`](crate::Error::InvalidArguments) if:
1256    /// - The path is empty
1257    ///
1258    /// Returns [`Error::CircuitExtensionFailed`](crate::Error::CircuitExtensionFailed) if:
1259    /// - The circuit doesn't exist
1260    /// - The circuit is in a state that doesn't allow extension
1261    /// - A specified relay is unknown or unavailable
1262    /// - The extension fails for any other reason
1263    ///
1264    /// # Example
1265    ///
1266    /// ```rust,no_run
1267    /// use stem_rs::controller::Controller;
1268    ///
1269    /// # async fn example() -> Result<(), stem_rs::Error> {
1270    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1271    /// controller.authenticate(None).await?;
1272    ///
1273    /// // Create a circuit and extend it
1274    /// let circuit_id = controller.new_circuit(None).await?;
1275    /// controller.extend_circuit(&circuit_id, &["$DDDD..."]).await?;
1276    /// # Ok(())
1277    /// # }
1278    /// ```
1279    ///
1280    /// # See Also
1281    ///
1282    /// - [`new_circuit`](Self::new_circuit): Create a new circuit
1283    /// - [`close_circuit`](Self::close_circuit): Close a circuit
1284    pub async fn extend_circuit(&mut self, id: &CircuitId, path: &[&str]) -> Result<(), Error> {
1285        if path.is_empty() {
1286            return Err(Error::InvalidArguments("path cannot be empty".to_string()));
1287        }
1288        let command = format!("EXTENDCIRCUIT {} {}", id.0, path.join(","));
1289        self.socket.send(&command).await?;
1290        let response = self.recv_response().await?;
1291
1292        if response.is_ok() {
1293            Ok(())
1294        } else {
1295            Err(Error::CircuitExtensionFailed(
1296                response.content().to_string(),
1297            ))
1298        }
1299    }
1300
1301    /// Closes an existing circuit.
1302    ///
1303    /// Tears down the specified circuit, closing all streams attached to it.
1304    ///
1305    /// # Arguments
1306    ///
1307    /// * `id` - The circuit ID to close
1308    ///
1309    /// # Errors
1310    ///
1311    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
1312    /// - The circuit doesn't exist
1313    /// - The circuit is already closed
1314    /// - Tor returns an error response
1315    ///
1316    /// # Example
1317    ///
1318    /// ```rust,no_run
1319    /// use stem_rs::controller::Controller;
1320    ///
1321    /// # async fn example() -> Result<(), stem_rs::Error> {
1322    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1323    /// controller.authenticate(None).await?;
1324    ///
1325    /// // Create and then close a circuit
1326    /// let circuit_id = controller.new_circuit(None).await?;
1327    /// controller.close_circuit(&circuit_id).await?;
1328    /// # Ok(())
1329    /// # }
1330    /// ```
1331    ///
1332    /// # See Also
1333    ///
1334    /// - [`new_circuit`](Self::new_circuit): Create a new circuit
1335    /// - [`get_circuits`](Self::get_circuits): List active circuits
1336    pub async fn close_circuit(&mut self, id: &CircuitId) -> Result<(), Error> {
1337        let command = format!("CLOSECIRCUIT {}", id.0);
1338        self.socket.send(&command).await?;
1339        let response = self.recv_response().await?;
1340
1341        if response.is_ok() {
1342            Ok(())
1343        } else {
1344            Err(Error::OperationFailed {
1345                code: response.status_code.to_string(),
1346                message: response.content().to_string(),
1347            })
1348        }
1349    }
1350
1351    /// Retrieves information about all active streams.
1352    ///
1353    /// Returns a list of all streams currently known to Tor, including
1354    /// their status, target, and circuit attachment.
1355    ///
1356    /// # Errors
1357    ///
1358    /// Returns an error if:
1359    /// - The GETINFO command fails
1360    /// - The stream status cannot be parsed
1361    ///
1362    /// # Example
1363    ///
1364    /// ```rust,no_run
1365    /// use stem_rs::controller::Controller;
1366    /// use stem_rs::StreamStatus;
1367    ///
1368    /// # async fn example() -> Result<(), stem_rs::Error> {
1369    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1370    /// controller.authenticate(None).await?;
1371    ///
1372    /// let streams = controller.get_streams().await?;
1373    /// for stream in streams {
1374    ///     println!("Stream {} -> {}:{} ({:?})",
1375    ///         stream.id, stream.target_host, stream.target_port, stream.status);
1376    /// }
1377    /// # Ok(())
1378    /// # }
1379    /// ```
1380    ///
1381    /// # See Also
1382    ///
1383    /// - [`Stream`]: Stream information structure
1384    /// - [`attach_stream`](Self::attach_stream): Attach a stream to a circuit
1385    /// - [`close_stream`](Self::close_stream): Close a stream
1386    pub async fn get_streams(&mut self) -> Result<Vec<Stream>, Error> {
1387        let response_str = self.get_info("stream-status").await?;
1388        parse_streams(&response_str)
1389    }
1390
1391    /// Attaches a stream to a specific circuit.
1392    ///
1393    /// Manually attaches a stream to a circuit. This is typically used when
1394    /// you want to control which circuit a stream uses, rather than letting
1395    /// Tor choose automatically.
1396    ///
1397    /// # Arguments
1398    ///
1399    /// * `stream_id` - The stream to attach
1400    /// * `circuit_id` - The circuit to attach the stream to
1401    ///
1402    /// # Preconditions
1403    ///
1404    /// - The stream must be in a state that allows attachment (typically NEW)
1405    /// - The circuit must be BUILT
1406    /// - The circuit's exit policy must allow the stream's target
1407    ///
1408    /// # Errors
1409    ///
1410    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
1411    /// - The stream doesn't exist
1412    /// - The circuit doesn't exist
1413    /// - The stream is not in an attachable state
1414    /// - The circuit cannot handle the stream's target
1415    ///
1416    /// # Example
1417    ///
1418    /// ```rust,no_run
1419    /// use stem_rs::controller::{Controller, CircuitId, StreamId};
1420    ///
1421    /// # async fn example() -> Result<(), stem_rs::Error> {
1422    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1423    /// controller.authenticate(None).await?;
1424    ///
1425    /// // Attach stream 1 to circuit 5
1426    /// let stream_id = StreamId::new("1");
1427    /// let circuit_id = CircuitId::new("5");
1428    /// controller.attach_stream(&stream_id, &circuit_id).await?;
1429    /// # Ok(())
1430    /// # }
1431    /// ```
1432    ///
1433    /// # See Also
1434    ///
1435    /// - [`get_streams`](Self::get_streams): List active streams
1436    /// - [`close_stream`](Self::close_stream): Close a stream
1437    pub async fn attach_stream(
1438        &mut self,
1439        stream_id: &StreamId,
1440        circuit_id: &CircuitId,
1441    ) -> Result<(), Error> {
1442        let command = format!("ATTACHSTREAM {} {}", stream_id.0, circuit_id.0);
1443        self.socket.send(&command).await?;
1444        let response = self.recv_response().await?;
1445
1446        if response.is_ok() {
1447            Ok(())
1448        } else {
1449            Err(Error::OperationFailed {
1450                code: response.status_code.to_string(),
1451                message: response.content().to_string(),
1452            })
1453        }
1454    }
1455
1456    /// Closes an existing stream.
1457    ///
1458    /// Terminates the specified stream with an optional reason code.
1459    ///
1460    /// # Arguments
1461    ///
1462    /// * `id` - The stream ID to close
1463    /// * `reason` - Optional reason code (defaults to 1 = MISC if not specified)
1464    ///
1465    /// # Reason Codes
1466    ///
1467    /// Common reason codes include:
1468    /// - 1: MISC (miscellaneous)
1469    /// - 2: RESOLVEFAILED (DNS resolution failed)
1470    /// - 3: CONNECTREFUSED (connection refused)
1471    /// - 4: EXITPOLICY (exit policy violation)
1472    /// - 5: DESTROY (circuit destroyed)
1473    /// - 6: DONE (stream finished normally)
1474    /// - 7: TIMEOUT (connection timeout)
1475    ///
1476    /// # Errors
1477    ///
1478    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
1479    /// - The stream doesn't exist
1480    /// - The stream is already closed
1481    /// - Tor returns an error response
1482    ///
1483    /// # Example
1484    ///
1485    /// ```rust,no_run
1486    /// use stem_rs::controller::{Controller, StreamId};
1487    ///
1488    /// # async fn example() -> Result<(), stem_rs::Error> {
1489    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1490    /// controller.authenticate(None).await?;
1491    ///
1492    /// // Close stream with default reason
1493    /// let stream_id = StreamId::new("1");
1494    /// controller.close_stream(&stream_id, None).await?;
1495    ///
1496    /// // Close stream with specific reason (DONE)
1497    /// controller.close_stream(&stream_id, Some(6)).await?;
1498    /// # Ok(())
1499    /// # }
1500    /// ```
1501    ///
1502    /// # See Also
1503    ///
1504    /// - [`get_streams`](Self::get_streams): List active streams
1505    /// - [`attach_stream`](Self::attach_stream): Attach a stream to a circuit
1506    pub async fn close_stream(&mut self, id: &StreamId, reason: Option<u8>) -> Result<(), Error> {
1507        let command = match reason {
1508            Some(r) => format!("CLOSESTREAM {} {}", id.0, r),
1509            None => format!("CLOSESTREAM {} 1", id.0),
1510        };
1511        self.socket.send(&command).await?;
1512        let response = self.recv_response().await?;
1513
1514        if response.is_ok() {
1515            Ok(())
1516        } else {
1517            Err(Error::OperationFailed {
1518                code: response.status_code.to_string(),
1519                message: response.content().to_string(),
1520            })
1521        }
1522    }
1523
1524    /// Maps one address to another for Tor connections.
1525    ///
1526    /// Creates an address mapping so that connections to the `from` address
1527    /// are redirected to the `to` address. This is useful for creating
1528    /// virtual addresses or redirecting traffic.
1529    ///
1530    /// # Arguments
1531    ///
1532    /// * `from` - The source address to map from
1533    /// * `to` - The destination address to map to
1534    ///
1535    /// # Returns
1536    ///
1537    /// Returns a `HashMap` containing the established mappings. The keys are
1538    /// the source addresses and values are the destination addresses.
1539    ///
1540    /// # Errors
1541    ///
1542    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
1543    /// - The address format is invalid
1544    /// - The mapping cannot be created
1545    /// - Tor returns an error response
1546    ///
1547    /// # Example
1548    ///
1549    /// ```rust,no_run
1550    /// use stem_rs::controller::Controller;
1551    ///
1552    /// # async fn example() -> Result<(), stem_rs::Error> {
1553    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1554    /// controller.authenticate(None).await?;
1555    ///
1556    /// // Map a hostname to a .onion address
1557    /// let mappings = controller.map_address(
1558    ///     "www.example.com",
1559    ///     "exampleonion.onion"
1560    /// ).await?;
1561    ///
1562    /// for (from, to) in mappings {
1563    ///     println!("{} -> {}", from, to);
1564    /// }
1565    /// # Ok(())
1566    /// # }
1567    /// ```
1568    pub async fn map_address(
1569        &mut self,
1570        from: &str,
1571        to: &str,
1572    ) -> Result<HashMap<String, String>, Error> {
1573        let command = format!("MAPADDRESS {}={}", from, to);
1574        self.socket.send(&command).await?;
1575        let response = self.recv_response().await?;
1576
1577        if !response.is_ok() {
1578            return Err(Error::OperationFailed {
1579                code: response.status_code.to_string(),
1580                message: response.content().to_string(),
1581            });
1582        }
1583
1584        let mut mappings = HashMap::new();
1585        for line in &response.lines {
1586            if let Some(eq_pos) = line.find('=') {
1587                let key = line[..eq_pos].to_string();
1588                let value = line[eq_pos + 1..].to_string();
1589                mappings.insert(key, value);
1590            }
1591        }
1592        Ok(mappings)
1593    }
1594
1595    /// Subscribes to asynchronous events from Tor.
1596    ///
1597    /// Configures which event types Tor should send to this controller.
1598    /// Events are received via [`recv_event`](Self::recv_event).
1599    ///
1600    /// # Arguments
1601    ///
1602    /// * `events` - List of event types to subscribe to
1603    ///
1604    /// # Event Types
1605    ///
1606    /// Common event types include:
1607    /// - [`EventType::Circ`](crate::EventType::Circ) - Circuit status changes
1608    /// - [`EventType::Stream`](crate::EventType::Stream) - Stream status changes
1609    /// - [`EventType::Bw`](crate::EventType::Bw) - Bandwidth usage
1610    /// - [`EventType::Notice`](crate::EventType::Notice) - Notice-level log messages
1611    /// - [`EventType::Warn`](crate::EventType::Warn) - Warning-level log messages
1612    ///
1613    /// # Errors
1614    ///
1615    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
1616    /// - An event type is not recognized
1617    /// - Tor returns an error response
1618    ///
1619    /// # Example
1620    ///
1621    /// ```rust,no_run
1622    /// use stem_rs::controller::Controller;
1623    /// use stem_rs::EventType;
1624    ///
1625    /// # async fn example() -> Result<(), stem_rs::Error> {
1626    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1627    /// controller.authenticate(None).await?;
1628    ///
1629    /// // Subscribe to circuit and bandwidth events
1630    /// controller.set_events(&[EventType::Circ, EventType::Bw]).await?;
1631    ///
1632    /// // Receive events
1633    /// loop {
1634    ///     let event = controller.recv_event().await?;
1635    ///     println!("Received event: {:?}", event);
1636    /// }
1637    /// # Ok(())
1638    /// # }
1639    /// ```
1640    ///
1641    /// # Clearing Subscriptions
1642    ///
1643    /// To stop receiving events, call with an empty slice:
1644    ///
1645    /// ```rust,no_run
1646    /// # use stem_rs::controller::Controller;
1647    /// # async fn example() -> Result<(), stem_rs::Error> {
1648    /// # let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1649    /// controller.set_events(&[]).await?; // Clear all subscriptions
1650    /// # Ok(())
1651    /// # }
1652    /// ```
1653    ///
1654    /// # See Also
1655    ///
1656    /// - [`recv_event`](Self::recv_event): Receive subscribed events
1657    /// - [`EventType`](crate::EventType): Available event types
1658    /// - [`events`](crate::events): Event parsing module
1659    pub async fn set_events(&mut self, events: &[EventType]) -> Result<(), Error> {
1660        let event_names: Vec<String> = events.iter().map(|e| e.to_string()).collect();
1661        let command = if event_names.is_empty() {
1662            "SETEVENTS".to_string()
1663        } else {
1664            format!("SETEVENTS {}", event_names.join(" "))
1665        };
1666        self.socket.send(&command).await?;
1667        let response = self.recv_response().await?;
1668
1669        if response.is_ok() {
1670            Ok(())
1671        } else {
1672            Err(Error::OperationFailed {
1673                code: response.status_code.to_string(),
1674                message: response.content().to_string(),
1675            })
1676        }
1677    }
1678
1679    /// Receives the next asynchronous event from Tor.
1680    ///
1681    /// Blocks until an event is available. Events must first be subscribed
1682    /// to using [`set_events`](Self::set_events).
1683    ///
1684    /// # Event Buffering
1685    ///
1686    /// Events that arrive while waiting for command responses are automatically
1687    /// buffered and returned by subsequent calls to this method.
1688    ///
1689    /// # Errors
1690    ///
1691    /// Returns [`Error::Protocol`](crate::Error::Protocol) if:
1692    /// - The received message is not an event (status code != 650)
1693    ///
1694    /// Returns [`Error::Socket`](crate::Error::Socket) if:
1695    /// - The connection is closed
1696    /// - A network error occurs
1697    ///
1698    /// # Example
1699    ///
1700    /// ```rust,no_run
1701    /// use stem_rs::controller::Controller;
1702    /// use stem_rs::EventType;
1703    /// use stem_rs::events::ParsedEvent;
1704    ///
1705    /// # async fn example() -> Result<(), stem_rs::Error> {
1706    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1707    /// controller.authenticate(None).await?;
1708    ///
1709    /// // Subscribe to bandwidth events
1710    /// controller.set_events(&[EventType::Bw]).await?;
1711    ///
1712    /// // Receive and process events
1713    /// loop {
1714    ///     match controller.recv_event().await? {
1715    ///         ParsedEvent::Bandwidth(bw) => {
1716    ///             println!("Bandwidth: {} read, {} written", bw.read, bw.written);
1717    ///         }
1718    ///         other => println!("Other event: {:?}", other),
1719    ///     }
1720    /// }
1721    /// # Ok(())
1722    /// # }
1723    /// ```
1724    ///
1725    /// # See Also
1726    ///
1727    /// - [`set_events`](Self::set_events): Subscribe to events
1728    /// - [`ParsedEvent`](crate::events::ParsedEvent): Event types
1729    pub async fn recv_event(&mut self) -> Result<ParsedEvent, Error> {
1730        let response = if let Some(buffered) = self.event_buffer.pop() {
1731            buffered
1732        } else {
1733            self.socket.recv().await?
1734        };
1735
1736        if response.status_code != 650 {
1737            return Err(Error::Protocol(format!(
1738                "expected async event (650), got {}",
1739                response.status_code
1740            )));
1741        }
1742
1743        let content = response.content();
1744        let (event_type, event_content) = content.split_once(' ').unwrap_or((content, ""));
1745
1746        let lines: Vec<String> = response
1747            .lines
1748            .iter()
1749            .skip(1)
1750            .filter(|l| !l.is_empty() && *l != "OK")
1751            .cloned()
1752            .collect();
1753
1754        ParsedEvent::parse(event_type, event_content, Some(&lines))
1755    }
1756
1757    /// Sends a raw command to Tor and returns the response.
1758    ///
1759    /// This is a low-level method for sending arbitrary control protocol
1760    /// commands. For most use cases, prefer the typed methods like
1761    /// [`get_info`](Self::get_info), [`signal`](Self::signal), etc.
1762    ///
1763    /// # Arguments
1764    ///
1765    /// * `command` - The raw command string to send
1766    ///
1767    /// # Errors
1768    ///
1769    /// Returns [`Error::OperationFailed`](crate::Error::OperationFailed) if:
1770    /// - Tor returns an error response
1771    ///
1772    /// Returns [`Error::Socket`](crate::Error::Socket) if:
1773    /// - The connection is closed
1774    /// - A network error occurs
1775    ///
1776    /// # Example
1777    ///
1778    /// ```rust,no_run
1779    /// use stem_rs::controller::Controller;
1780    ///
1781    /// # async fn example() -> Result<(), stem_rs::Error> {
1782    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1783    /// controller.authenticate(None).await?;
1784    ///
1785    /// // Send a raw GETINFO command
1786    /// let response = controller.msg("GETINFO version").await?;
1787    /// println!("Raw response: {}", response);
1788    /// # Ok(())
1789    /// # }
1790    /// ```
1791    ///
1792    /// # See Also
1793    ///
1794    /// - [`get_info`](Self::get_info): Typed GETINFO wrapper
1795    /// - [`signal`](Self::signal): Typed SIGNAL wrapper
1796    pub async fn msg(&mut self, command: &str) -> Result<String, Error> {
1797        self.socket.send(command).await?;
1798        let response = self.recv_response().await?;
1799
1800        if !response.is_ok() {
1801            return Err(Error::OperationFailed {
1802                code: response.status_code.to_string(),
1803                message: response.content().to_string(),
1804            });
1805        }
1806
1807        Ok(response.raw_content())
1808    }
1809}
1810
1811/// Parses circuit status output from GETINFO circuit-status.
1812///
1813/// Converts the multi-line circuit status response into a vector of [`Circuit`] structs.
1814fn parse_circuits(content: &str) -> Result<Vec<Circuit>, Error> {
1815    let mut circuits = Vec::new();
1816
1817    for line in content.lines() {
1818        let line = line.trim();
1819        if line.is_empty() {
1820            continue;
1821        }
1822
1823        let mut parts = line.split_whitespace();
1824        let id = parts.next().ok_or_else(|| Error::Parse {
1825            location: "circuit".to_string(),
1826            reason: "missing circuit id".to_string(),
1827        })?;
1828
1829        let status_str = parts.next().ok_or_else(|| Error::Parse {
1830            location: "circuit".to_string(),
1831            reason: "missing circuit status".to_string(),
1832        })?;
1833
1834        let status = parse_circ_status(status_str)?;
1835
1836        let mut path = Vec::new();
1837        if let Some(path_str) = parts.next() {
1838            if !path_str.starts_with("BUILD_FLAGS=")
1839                && !path_str.starts_with("PURPOSE=")
1840                && !path_str.starts_with("TIME_CREATED=")
1841            {
1842                for relay in path_str.split(',') {
1843                    let relay_info = parse_relay_info(relay);
1844                    path.push(relay_info);
1845                }
1846            }
1847        }
1848
1849        circuits.push(Circuit {
1850            id: CircuitId::new(id),
1851            status,
1852            path,
1853        });
1854    }
1855
1856    Ok(circuits)
1857}
1858
1859/// Parses a circuit status string into a [`CircStatus`] enum.
1860fn parse_circ_status(s: &str) -> Result<CircStatus, Error> {
1861    match s.to_uppercase().as_str() {
1862        "LAUNCHED" => Ok(CircStatus::Launched),
1863        "BUILT" => Ok(CircStatus::Built),
1864        "GUARD_WAIT" => Ok(CircStatus::GuardWait),
1865        "EXTENDED" => Ok(CircStatus::Extended),
1866        "FAILED" => Ok(CircStatus::Failed),
1867        "CLOSED" => Ok(CircStatus::Closed),
1868        _ => Err(Error::Parse {
1869            location: "circuit status".to_string(),
1870            reason: format!("unknown status: {}", s),
1871        }),
1872    }
1873}
1874
1875/// Parses a relay specification string into a [`RelayInfo`] struct.
1876///
1877/// Handles formats like `$FINGERPRINT~Nickname` or just `$FINGERPRINT`.
1878fn parse_relay_info(s: &str) -> RelayInfo {
1879    if let Some((fingerprint, nickname)) = s.split_once('~') {
1880        RelayInfo {
1881            fingerprint: fingerprint.trim_start_matches('$').to_string(),
1882            nickname: Some(nickname.to_string()),
1883        }
1884    } else {
1885        RelayInfo {
1886            fingerprint: s.trim_start_matches('$').to_string(),
1887            nickname: None,
1888        }
1889    }
1890}
1891
1892/// Parses stream status output from GETINFO stream-status.
1893///
1894/// Converts the multi-line stream status response into a vector of [`Stream`] structs.
1895fn parse_streams(content: &str) -> Result<Vec<Stream>, Error> {
1896    let mut streams = Vec::new();
1897
1898    for line in content.lines() {
1899        let line = line.trim();
1900        if line.is_empty() {
1901            continue;
1902        }
1903
1904        let mut parts = line.split_whitespace();
1905        let id = parts.next().ok_or_else(|| Error::Parse {
1906            location: "stream".to_string(),
1907            reason: "missing stream id".to_string(),
1908        })?;
1909
1910        let status_str = parts.next().ok_or_else(|| Error::Parse {
1911            location: "stream".to_string(),
1912            reason: "missing stream status".to_string(),
1913        })?;
1914
1915        let status = parse_stream_status(status_str)?;
1916
1917        let circuit_id_str = parts.next().ok_or_else(|| Error::Parse {
1918            location: "stream".to_string(),
1919            reason: "missing circuit id".to_string(),
1920        })?;
1921
1922        let circuit_id = if circuit_id_str == "0" {
1923            None
1924        } else {
1925            Some(CircuitId::new(circuit_id_str))
1926        };
1927
1928        let target = parts.next().ok_or_else(|| Error::Parse {
1929            location: "stream".to_string(),
1930            reason: "missing target".to_string(),
1931        })?;
1932
1933        let (target_host, target_port) = parse_target(target)?;
1934
1935        streams.push(Stream {
1936            id: StreamId::new(id),
1937            status,
1938            circuit_id,
1939            target_host,
1940            target_port,
1941        });
1942    }
1943
1944    Ok(streams)
1945}
1946
1947/// Parses a stream status string into a [`StreamStatus`] enum.
1948fn parse_stream_status(s: &str) -> Result<StreamStatus, Error> {
1949    match s.to_uppercase().as_str() {
1950        "NEW" => Ok(StreamStatus::New),
1951        "NEWRESOLVE" => Ok(StreamStatus::NewResolve),
1952        "REMAP" => Ok(StreamStatus::Remap),
1953        "SENTCONNECT" => Ok(StreamStatus::SentConnect),
1954        "SENTRESOLVE" => Ok(StreamStatus::SentResolve),
1955        "SUCCEEDED" => Ok(StreamStatus::Succeeded),
1956        "FAILED" => Ok(StreamStatus::Failed),
1957        "DETACHED" => Ok(StreamStatus::Detached),
1958        "CONTROLLER_WAIT" => Ok(StreamStatus::ControllerWait),
1959        "CLOSED" => Ok(StreamStatus::Closed),
1960        _ => Err(Error::Parse {
1961            location: "stream status".to_string(),
1962            reason: format!("unknown status: {}", s),
1963        }),
1964    }
1965}
1966
1967/// Parses a target address string into host and port components.
1968///
1969/// Handles formats like `host:port` or just `host` (port defaults to 0).
1970fn parse_target(target: &str) -> Result<(String, u16), Error> {
1971    if let Some(colon_pos) = target.rfind(':') {
1972        let host = target[..colon_pos].to_string();
1973        let port_str = &target[colon_pos + 1..];
1974        let port: u16 = port_str.parse().map_err(|_| Error::Parse {
1975            location: "stream target".to_string(),
1976            reason: format!("invalid port: {}", port_str),
1977        })?;
1978        Ok((host, port))
1979    } else {
1980        Ok((target.to_string(), 0))
1981    }
1982}
1983
1984#[cfg(test)]
1985mod tests {
1986    use super::*;
1987
1988    #[test]
1989    fn test_circuit_id_display() {
1990        let id = CircuitId::new("123");
1991        assert_eq!(id.to_string(), "123");
1992    }
1993
1994    #[test]
1995    fn test_stream_id_display() {
1996        let id = StreamId::new("456");
1997        assert_eq!(id.to_string(), "456");
1998    }
1999
2000    #[test]
2001    fn test_parse_circ_status() {
2002        assert_eq!(parse_circ_status("LAUNCHED").unwrap(), CircStatus::Launched);
2003        assert_eq!(parse_circ_status("BUILT").unwrap(), CircStatus::Built);
2004        assert_eq!(
2005            parse_circ_status("GUARD_WAIT").unwrap(),
2006            CircStatus::GuardWait
2007        );
2008        assert_eq!(parse_circ_status("EXTENDED").unwrap(), CircStatus::Extended);
2009        assert_eq!(parse_circ_status("FAILED").unwrap(), CircStatus::Failed);
2010        assert_eq!(parse_circ_status("CLOSED").unwrap(), CircStatus::Closed);
2011        assert_eq!(parse_circ_status("launched").unwrap(), CircStatus::Launched);
2012        assert!(parse_circ_status("UNKNOWN").is_err());
2013    }
2014
2015    #[test]
2016    fn test_parse_stream_status() {
2017        assert_eq!(parse_stream_status("NEW").unwrap(), StreamStatus::New);
2018        assert_eq!(
2019            parse_stream_status("NEWRESOLVE").unwrap(),
2020            StreamStatus::NewResolve
2021        );
2022        assert_eq!(parse_stream_status("REMAP").unwrap(), StreamStatus::Remap);
2023        assert_eq!(
2024            parse_stream_status("SENTCONNECT").unwrap(),
2025            StreamStatus::SentConnect
2026        );
2027        assert_eq!(
2028            parse_stream_status("SENTRESOLVE").unwrap(),
2029            StreamStatus::SentResolve
2030        );
2031        assert_eq!(
2032            parse_stream_status("SUCCEEDED").unwrap(),
2033            StreamStatus::Succeeded
2034        );
2035        assert_eq!(parse_stream_status("FAILED").unwrap(), StreamStatus::Failed);
2036        assert_eq!(
2037            parse_stream_status("DETACHED").unwrap(),
2038            StreamStatus::Detached
2039        );
2040        assert_eq!(
2041            parse_stream_status("CONTROLLER_WAIT").unwrap(),
2042            StreamStatus::ControllerWait
2043        );
2044        assert_eq!(parse_stream_status("CLOSED").unwrap(), StreamStatus::Closed);
2045        assert!(parse_stream_status("UNKNOWN").is_err());
2046    }
2047
2048    #[test]
2049    fn test_parse_relay_info_with_nickname() {
2050        let info = parse_relay_info("$ABCD1234~MyRelay");
2051        assert_eq!(info.fingerprint, "ABCD1234");
2052        assert_eq!(info.nickname, Some("MyRelay".to_string()));
2053    }
2054
2055    #[test]
2056    fn test_parse_relay_info_without_nickname() {
2057        let info = parse_relay_info("$ABCD1234");
2058        assert_eq!(info.fingerprint, "ABCD1234");
2059        assert_eq!(info.nickname, None);
2060    }
2061
2062    #[test]
2063    fn test_parse_relay_info_no_dollar() {
2064        let info = parse_relay_info("ABCD1234~MyRelay");
2065        assert_eq!(info.fingerprint, "ABCD1234");
2066        assert_eq!(info.nickname, Some("MyRelay".to_string()));
2067    }
2068
2069    #[test]
2070    fn test_parse_target_with_port() {
2071        let (host, port) = parse_target("example.com:443").unwrap();
2072        assert_eq!(host, "example.com");
2073        assert_eq!(port, 443);
2074    }
2075
2076    #[test]
2077    fn test_parse_target_ipv4_with_port() {
2078        let (host, port) = parse_target("192.168.1.1:80").unwrap();
2079        assert_eq!(host, "192.168.1.1");
2080        assert_eq!(port, 80);
2081    }
2082
2083    #[test]
2084    fn test_parse_target_without_port() {
2085        let (host, port) = parse_target("example.com").unwrap();
2086        assert_eq!(host, "example.com");
2087        assert_eq!(port, 0);
2088    }
2089
2090    #[test]
2091    fn test_parse_circuits_empty() {
2092        let circuits = parse_circuits("").unwrap();
2093        assert!(circuits.is_empty());
2094    }
2095
2096    #[test]
2097    fn test_parse_circuits_single() {
2098        let content = "1 BUILT $AAAA~Guard,$BBBB~Middle,$CCCC~Exit";
2099        let circuits = parse_circuits(content).unwrap();
2100        assert_eq!(circuits.len(), 1);
2101        assert_eq!(circuits[0].id.0, "1");
2102        assert_eq!(circuits[0].status, CircStatus::Built);
2103        assert_eq!(circuits[0].path.len(), 3);
2104        assert_eq!(circuits[0].path[0].fingerprint, "AAAA");
2105        assert_eq!(circuits[0].path[0].nickname, Some("Guard".to_string()));
2106    }
2107
2108    #[test]
2109    fn test_parse_circuits_multiple() {
2110        let content = "1 BUILT $AAAA~Guard,$BBBB~Exit\n2 LAUNCHED\n3 EXTENDED $CCCC~Relay";
2111        let circuits = parse_circuits(content).unwrap();
2112        assert_eq!(circuits.len(), 3);
2113        assert_eq!(circuits[0].status, CircStatus::Built);
2114        assert_eq!(circuits[1].status, CircStatus::Launched);
2115        assert_eq!(circuits[2].status, CircStatus::Extended);
2116    }
2117
2118    #[test]
2119    fn test_parse_circuits_with_flags() {
2120        let content = "1 BUILT $AAAA~Guard BUILD_FLAGS=IS_INTERNAL PURPOSE=GENERAL";
2121        let circuits = parse_circuits(content).unwrap();
2122        assert_eq!(circuits.len(), 1);
2123        assert_eq!(circuits[0].path.len(), 1);
2124    }
2125
2126    #[test]
2127    fn test_parse_streams_empty() {
2128        let streams = parse_streams("").unwrap();
2129        assert!(streams.is_empty());
2130    }
2131
2132    #[test]
2133    fn test_parse_streams_single() {
2134        let content = "1 SUCCEEDED 5 www.example.com:443";
2135        let streams = parse_streams(content).unwrap();
2136        assert_eq!(streams.len(), 1);
2137        assert_eq!(streams[0].id.0, "1");
2138        assert_eq!(streams[0].status, StreamStatus::Succeeded);
2139        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("5")));
2140        assert_eq!(streams[0].target_host, "www.example.com");
2141        assert_eq!(streams[0].target_port, 443);
2142    }
2143
2144    #[test]
2145    fn test_parse_streams_no_circuit() {
2146        let content = "1 NEW 0 www.example.com:80";
2147        let streams = parse_streams(content).unwrap();
2148        assert_eq!(streams.len(), 1);
2149        assert_eq!(streams[0].circuit_id, None);
2150    }
2151
2152    #[test]
2153    fn test_parse_streams_multiple() {
2154        let content = "1 SUCCEEDED 5 www.example.com:443\n2 NEW 0 api.example.com:80";
2155        let streams = parse_streams(content).unwrap();
2156        assert_eq!(streams.len(), 2);
2157    }
2158
2159    #[test]
2160    fn test_circuit_id_equality() {
2161        let id1 = CircuitId::new("123");
2162        let id2 = CircuitId::new("123");
2163        let id3 = CircuitId::new("456");
2164        assert_eq!(id1, id2);
2165        assert_ne!(id1, id3);
2166    }
2167
2168    #[test]
2169    fn test_stream_id_equality() {
2170        let id1 = StreamId::new("123");
2171        let id2 = StreamId::new("123");
2172        let id3 = StreamId::new("456");
2173        assert_eq!(id1, id2);
2174        assert_ne!(id1, id3);
2175    }
2176}
2177
2178#[cfg(test)]
2179mod stem_tests {
2180    use super::*;
2181
2182    #[test]
2183    fn test_parse_circ_path_empty() {
2184        let circuits = parse_circuits("").unwrap();
2185        assert!(circuits.is_empty());
2186    }
2187
2188    #[test]
2189    fn test_parse_circ_path_with_fingerprint_and_nickname() {
2190        let content = "1 BUILT $999A226EBED397F331B612FE1E4CFAE5C1F201BA~piyaz";
2191        let circuits = parse_circuits(content).unwrap();
2192        assert_eq!(circuits.len(), 1);
2193        assert_eq!(circuits[0].path.len(), 1);
2194        assert_eq!(
2195            circuits[0].path[0].fingerprint,
2196            "999A226EBED397F331B612FE1E4CFAE5C1F201BA"
2197        );
2198        assert_eq!(circuits[0].path[0].nickname, Some("piyaz".to_string()));
2199    }
2200
2201    #[test]
2202    fn test_parse_circ_path_multiple_relays() {
2203        let content =
2204            "1 BUILT $E57A476CD4DFBD99B4EE52A100A58610AD6E80B9,$AAAA,$BBBB~PrivacyRepublic14";
2205        let circuits = parse_circuits(content).unwrap();
2206        assert_eq!(circuits.len(), 1);
2207        assert_eq!(circuits[0].path.len(), 3);
2208        assert_eq!(
2209            circuits[0].path[0].fingerprint,
2210            "E57A476CD4DFBD99B4EE52A100A58610AD6E80B9"
2211        );
2212        assert_eq!(circuits[0].path[0].nickname, None);
2213        assert_eq!(circuits[0].path[2].fingerprint, "BBBB");
2214        assert_eq!(
2215            circuits[0].path[2].nickname,
2216            Some("PrivacyRepublic14".to_string())
2217        );
2218    }
2219
2220    #[test]
2221    fn test_get_streams_parsing() {
2222        let content =
2223            "1 NEW 4 10.10.10.1:80\n2 SUCCEEDED 4 10.10.10.1:80\n3 SUCCEEDED 4 10.10.10.1:80";
2224        let streams = parse_streams(content).unwrap();
2225        assert_eq!(streams.len(), 3);
2226
2227        assert_eq!(streams[0].id.0, "1");
2228        assert_eq!(streams[0].status, StreamStatus::New);
2229        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("4")));
2230        assert_eq!(streams[0].target_host, "10.10.10.1");
2231        assert_eq!(streams[0].target_port, 80);
2232
2233        assert_eq!(streams[1].id.0, "2");
2234        assert_eq!(streams[1].status, StreamStatus::Succeeded);
2235
2236        assert_eq!(streams[2].id.0, "3");
2237        assert_eq!(streams[2].status, StreamStatus::Succeeded);
2238    }
2239
2240    #[test]
2241    fn test_circuit_status_parsing() {
2242        let test_cases = [
2243            ("LAUNCHED", CircStatus::Launched),
2244            ("BUILT", CircStatus::Built),
2245            ("GUARD_WAIT", CircStatus::GuardWait),
2246            ("EXTENDED", CircStatus::Extended),
2247            ("FAILED", CircStatus::Failed),
2248            ("CLOSED", CircStatus::Closed),
2249        ];
2250
2251        for (input, expected) in test_cases {
2252            assert_eq!(parse_circ_status(input).unwrap(), expected);
2253        }
2254    }
2255
2256    #[test]
2257    fn test_stream_status_parsing() {
2258        let test_cases = [
2259            ("NEW", StreamStatus::New),
2260            ("NEWRESOLVE", StreamStatus::NewResolve),
2261            ("REMAP", StreamStatus::Remap),
2262            ("SENTCONNECT", StreamStatus::SentConnect),
2263            ("SENTRESOLVE", StreamStatus::SentResolve),
2264            ("SUCCEEDED", StreamStatus::Succeeded),
2265            ("FAILED", StreamStatus::Failed),
2266            ("DETACHED", StreamStatus::Detached),
2267            ("CONTROLLER_WAIT", StreamStatus::ControllerWait),
2268            ("CLOSED", StreamStatus::Closed),
2269        ];
2270
2271        for (input, expected) in test_cases {
2272            assert_eq!(parse_stream_status(input).unwrap(), expected);
2273        }
2274    }
2275
2276    #[test]
2277    fn test_parse_target_various() {
2278        let test_cases = [
2279            ("www.example.com:443", ("www.example.com", 443)),
2280            ("192.168.1.1:80", ("192.168.1.1", 80)),
2281            ("10.10.10.1:8080", ("10.10.10.1", 8080)),
2282            ("[::1]:443", ("[::1]", 443)),
2283        ];
2284
2285        for (input, (expected_host, expected_port)) in test_cases {
2286            let (host, port) = parse_target(input).unwrap();
2287            assert_eq!(host, expected_host);
2288            assert_eq!(port, expected_port);
2289        }
2290    }
2291
2292    #[test]
2293    fn test_parse_circuits_with_build_flags() {
2294        let content = "1 BUILT $AAAA~Guard,$BBBB~Exit BUILD_FLAGS=IS_INTERNAL,NEED_CAPACITY PURPOSE=GENERAL TIME_CREATED=2023-01-01T00:00:00";
2295        let circuits = parse_circuits(content).unwrap();
2296        assert_eq!(circuits.len(), 1);
2297        assert_eq!(circuits[0].status, CircStatus::Built);
2298        assert_eq!(circuits[0].path.len(), 2);
2299    }
2300
2301    #[test]
2302    fn test_parse_circuits_launched_no_path() {
2303        let content = "1 LAUNCHED BUILD_FLAGS=NEED_CAPACITY PURPOSE=GENERAL";
2304        let circuits = parse_circuits(content).unwrap();
2305        assert_eq!(circuits.len(), 1);
2306        assert_eq!(circuits[0].status, CircStatus::Launched);
2307        assert!(circuits[0].path.is_empty());
2308    }
2309
2310    #[test]
2311    fn test_parse_streams_detached() {
2312        let content = "1 DETACHED 0 www.example.com:443";
2313        let streams = parse_streams(content).unwrap();
2314        assert_eq!(streams.len(), 1);
2315        assert_eq!(streams[0].status, StreamStatus::Detached);
2316        assert_eq!(streams[0].circuit_id, None);
2317    }
2318
2319    #[test]
2320    fn test_relay_info_parsing_variations() {
2321        let test_cases = [
2322            ("$ABCD1234~MyRelay", "ABCD1234", Some("MyRelay")),
2323            ("$ABCD1234", "ABCD1234", None),
2324            ("ABCD1234~MyRelay", "ABCD1234", Some("MyRelay")),
2325            ("ABCD1234", "ABCD1234", None),
2326        ];
2327
2328        for (input, expected_fp, expected_nick) in test_cases {
2329            let info = parse_relay_info(input);
2330            assert_eq!(info.fingerprint, expected_fp);
2331            assert_eq!(info.nickname, expected_nick.map(|s| s.to_string()));
2332        }
2333    }
2334
2335    #[test]
2336    fn test_circuit_id_hash() {
2337        use std::collections::HashSet;
2338        let mut set = HashSet::new();
2339        set.insert(CircuitId::new("1"));
2340        set.insert(CircuitId::new("2"));
2341        set.insert(CircuitId::new("1"));
2342        assert_eq!(set.len(), 2);
2343    }
2344
2345    #[test]
2346    fn test_stream_id_hash() {
2347        use std::collections::HashSet;
2348        let mut set = HashSet::new();
2349        set.insert(StreamId::new("1"));
2350        set.insert(StreamId::new("2"));
2351        set.insert(StreamId::new("1"));
2352        assert_eq!(set.len(), 2);
2353    }
2354
2355    #[test]
2356    fn test_parse_circuits_real_world_example() {
2357        let content = r#"7 BUILT $5CECC5C30ACC4B3DE462792323967087CC53D947~Quetzalcoatl,$51E1CF613FD6F9F11FE24743C91D6F9981807D82~DigiGesTor4e3,$B06F093A3D4DFAD3E923F4F28A74901BD4F74EB1~torserversNet BUILD_FLAGS=IS_INTERNAL,NEED_CAPACITY,NEED_UPTIME PURPOSE=HS_CLIENT_HSDIR HS_STATE=HSCI_CONNECTING TIME_CREATED=2023-06-15T10:30:45.123456"#;
2358        let circuits = parse_circuits(content).unwrap();
2359        assert_eq!(circuits.len(), 1);
2360        assert_eq!(circuits[0].id.0, "7");
2361        assert_eq!(circuits[0].status, CircStatus::Built);
2362        assert_eq!(circuits[0].path.len(), 3);
2363        assert_eq!(
2364            circuits[0].path[0].nickname,
2365            Some("Quetzalcoatl".to_string())
2366        );
2367        assert_eq!(
2368            circuits[0].path[1].nickname,
2369            Some("DigiGesTor4e3".to_string())
2370        );
2371        assert_eq!(
2372            circuits[0].path[2].nickname,
2373            Some("torserversNet".to_string())
2374        );
2375    }
2376
2377    #[test]
2378    fn test_parse_streams_real_world_example() {
2379        let content =
2380            "42 SUCCEEDED 7 www.torproject.org:443 SOURCE_ADDR=127.0.0.1:12345 PURPOSE=USER";
2381        let streams = parse_streams(content).unwrap();
2382        assert_eq!(streams.len(), 1);
2383        assert_eq!(streams[0].id.0, "42");
2384        assert_eq!(streams[0].status, StreamStatus::Succeeded);
2385        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("7")));
2386        assert_eq!(streams[0].target_host, "www.torproject.org");
2387        assert_eq!(streams[0].target_port, 443);
2388    }
2389}