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    /// Creates an ephemeral hidden service.
1811    ///
1812    /// Unlike file-based hidden services, ephemeral services don't touch disk
1813    /// and are the recommended way to create hidden services programmatically.
1814    ///
1815    /// # Arguments
1816    ///
1817    /// * `ports` - Mapping of virtual ports to local targets (e.g., `[(80, "127.0.0.1:8080")]`)
1818    /// * `key_type` - Type of key: `"NEW"` to generate, `"RSA1024"`, or `"ED25519-V3"`
1819    /// * `key_content` - Key content or type to generate (`"BEST"`, `"RSA1024"`, `"ED25519-V3"`)
1820    /// * `flags` - Optional flags like `"Detach"`, `"DiscardPK"`, `"BasicAuth"`, `"MaxStreamsCloseCircuit"`
1821    ///
1822    /// # Returns
1823    ///
1824    /// Returns an [`AddOnionResponse`] containing:
1825    /// - `service_id`: The onion address (without `.onion` suffix)
1826    /// - `private_key`: The private key (unless `DiscardPK` flag was set)
1827    /// - `private_key_type`: The key type (e.g., `"ED25519-V3"`)
1828    ///
1829    /// # Example
1830    ///
1831    /// ```rust,no_run
1832    /// use stem_rs::controller::Controller;
1833    ///
1834    /// # async fn example() -> Result<(), stem_rs::Error> {
1835    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1836    /// controller.authenticate(None).await?;
1837    ///
1838    /// // Create a v3 hidden service mapping port 80 to local port 8080
1839    /// let response = controller.create_ephemeral_hidden_service(
1840    ///     &[(80, "127.0.0.1:8080")],
1841    ///     "NEW",
1842    ///     "ED25519-V3",
1843    ///     &[],
1844    /// ).await?;
1845    ///
1846    /// println!("Hidden service: {}.onion", response.service_id);
1847    /// # Ok(())
1848    /// # }
1849    /// ```
1850    ///
1851    /// # See Also
1852    ///
1853    /// - [`remove_ephemeral_hidden_service`](Self::remove_ephemeral_hidden_service): Remove the service
1854    pub async fn create_ephemeral_hidden_service(
1855        &mut self,
1856        ports: &[(u16, &str)],
1857        key_type: &str,
1858        key_content: &str,
1859        flags: &[&str],
1860    ) -> Result<AddOnionResponse, Error> {
1861        let mut request = format!("ADD_ONION {}:{}", key_type, key_content);
1862
1863        if !flags.is_empty() {
1864            request.push_str(&format!(" Flags={}", flags.join(",")));
1865        }
1866
1867        for (virt_port, target) in ports {
1868            request.push_str(&format!(" Port={},{}", virt_port, target));
1869        }
1870
1871        self.socket.send(&request).await?;
1872        let response = self.recv_response().await?;
1873
1874        if !response.is_ok() {
1875            return Err(Error::OperationFailed {
1876                code: response.status_code.to_string(),
1877                message: response.content().to_string(),
1878            });
1879        }
1880
1881        parse_add_onion_response(&response.all_content())
1882    }
1883
1884    /// Removes an ephemeral hidden service.
1885    ///
1886    /// Discontinues a hidden service that was created with
1887    /// [`create_ephemeral_hidden_service`](Self::create_ephemeral_hidden_service).
1888    ///
1889    /// # Arguments
1890    ///
1891    /// * `service_id` - The onion address without the `.onion` suffix
1892    ///
1893    /// # Returns
1894    ///
1895    /// Returns `true` if the service was removed, `false` if it wasn't running.
1896    ///
1897    /// # Example
1898    ///
1899    /// ```rust,no_run
1900    /// use stem_rs::controller::Controller;
1901    ///
1902    /// # async fn example() -> Result<(), stem_rs::Error> {
1903    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1904    /// controller.authenticate(None).await?;
1905    ///
1906    /// // Create and then remove a hidden service
1907    /// let response = controller.create_ephemeral_hidden_service(
1908    ///     &[(80, "127.0.0.1:8080")],
1909    ///     "NEW",
1910    ///     "BEST",
1911    ///     &[],
1912    /// ).await?;
1913    ///
1914    /// controller.remove_ephemeral_hidden_service(&response.service_id).await?;
1915    /// # Ok(())
1916    /// # }
1917    /// ```
1918    pub async fn remove_ephemeral_hidden_service(&mut self, service_id: &str) -> Result<bool, Error> {
1919        let command = format!("DEL_ONION {}", service_id);
1920        match self.msg(&command).await {
1921            Ok(_) => Ok(true),
1922            Err(Error::OperationFailed { code, message }) => {
1923                if message.contains("Unknown Onion Service") {
1924                    Ok(false)
1925                } else {
1926                    Err(Error::OperationFailed { code, message })
1927                }
1928            }
1929            Err(e) => Err(e),
1930        }
1931    }
1932}
1933
1934/// Response from ADD_ONION command.
1935///
1936/// Contains the service ID and optionally the private key for the hidden service.
1937#[derive(Debug, Clone)]
1938pub struct AddOnionResponse {
1939    /// The onion address without the `.onion` suffix.
1940    pub service_id: String,
1941    /// The private key (base64 encoded), if not discarded.
1942    pub private_key: Option<String>,
1943    /// The type of private key (e.g., `"ED25519-V3"`, `"RSA1024"`).
1944    pub private_key_type: Option<String>,
1945}
1946
1947/// Parses the response from an ADD_ONION command.
1948fn parse_add_onion_response(content: &str) -> Result<AddOnionResponse, Error> {
1949    let mut service_id = None;
1950    let mut private_key = None;
1951    let mut private_key_type = None;
1952
1953    for line in content.lines() {
1954        let line = line.trim();
1955        if let Some(value) = line.strip_prefix("ServiceID=") {
1956            service_id = Some(value.to_string());
1957        } else if let Some(value) = line.strip_prefix("PrivateKey=") {
1958            if let Some((key_type, key_content)) = value.split_once(':') {
1959                private_key_type = Some(key_type.to_string());
1960                private_key = Some(key_content.to_string());
1961            }
1962        }
1963    }
1964
1965    let service_id = service_id.ok_or_else(|| Error::Parse {
1966        location: "ADD_ONION response".to_string(),
1967        reason: "missing ServiceID".to_string(),
1968    })?;
1969
1970    Ok(AddOnionResponse {
1971        service_id,
1972        private_key,
1973        private_key_type,
1974    })
1975}
1976
1977/// Parses circuit status output from GETINFO circuit-status.
1978///
1979/// Converts the multi-line circuit status response into a vector of [`Circuit`] structs.
1980fn parse_circuits(content: &str) -> Result<Vec<Circuit>, Error> {
1981    let mut circuits = Vec::new();
1982
1983    for line in content.lines() {
1984        let line = line.trim();
1985        if line.is_empty() {
1986            continue;
1987        }
1988
1989        let mut parts = line.split_whitespace();
1990        let id = parts.next().ok_or_else(|| Error::Parse {
1991            location: "circuit".to_string(),
1992            reason: "missing circuit id".to_string(),
1993        })?;
1994
1995        let status_str = parts.next().ok_or_else(|| Error::Parse {
1996            location: "circuit".to_string(),
1997            reason: "missing circuit status".to_string(),
1998        })?;
1999
2000        let status = parse_circ_status(status_str)?;
2001
2002        let mut path = Vec::new();
2003        if let Some(path_str) = parts.next() {
2004            if !path_str.starts_with("BUILD_FLAGS=")
2005                && !path_str.starts_with("PURPOSE=")
2006                && !path_str.starts_with("TIME_CREATED=")
2007            {
2008                for relay in path_str.split(',') {
2009                    let relay_info = parse_relay_info(relay);
2010                    path.push(relay_info);
2011                }
2012            }
2013        }
2014
2015        circuits.push(Circuit {
2016            id: CircuitId::new(id),
2017            status,
2018            path,
2019        });
2020    }
2021
2022    Ok(circuits)
2023}
2024
2025/// Parses a circuit status string into a [`CircStatus`] enum.
2026fn parse_circ_status(s: &str) -> Result<CircStatus, Error> {
2027    match s.to_uppercase().as_str() {
2028        "LAUNCHED" => Ok(CircStatus::Launched),
2029        "BUILT" => Ok(CircStatus::Built),
2030        "GUARD_WAIT" => Ok(CircStatus::GuardWait),
2031        "EXTENDED" => Ok(CircStatus::Extended),
2032        "FAILED" => Ok(CircStatus::Failed),
2033        "CLOSED" => Ok(CircStatus::Closed),
2034        _ => Err(Error::Parse {
2035            location: "circuit status".to_string(),
2036            reason: format!("unknown status: {}", s),
2037        }),
2038    }
2039}
2040
2041/// Parses a relay specification string into a [`RelayInfo`] struct.
2042///
2043/// Handles formats like `$FINGERPRINT~Nickname` or just `$FINGERPRINT`.
2044fn parse_relay_info(s: &str) -> RelayInfo {
2045    if let Some((fingerprint, nickname)) = s.split_once('~') {
2046        RelayInfo {
2047            fingerprint: fingerprint.trim_start_matches('$').to_string(),
2048            nickname: Some(nickname.to_string()),
2049        }
2050    } else {
2051        RelayInfo {
2052            fingerprint: s.trim_start_matches('$').to_string(),
2053            nickname: None,
2054        }
2055    }
2056}
2057
2058/// Parses stream status output from GETINFO stream-status.
2059///
2060/// Converts the multi-line stream status response into a vector of [`Stream`] structs.
2061fn parse_streams(content: &str) -> Result<Vec<Stream>, Error> {
2062    let mut streams = Vec::new();
2063
2064    for line in content.lines() {
2065        let line = line.trim();
2066        if line.is_empty() {
2067            continue;
2068        }
2069
2070        let mut parts = line.split_whitespace();
2071        let id = parts.next().ok_or_else(|| Error::Parse {
2072            location: "stream".to_string(),
2073            reason: "missing stream id".to_string(),
2074        })?;
2075
2076        let status_str = parts.next().ok_or_else(|| Error::Parse {
2077            location: "stream".to_string(),
2078            reason: "missing stream status".to_string(),
2079        })?;
2080
2081        let status = parse_stream_status(status_str)?;
2082
2083        let circuit_id_str = parts.next().ok_or_else(|| Error::Parse {
2084            location: "stream".to_string(),
2085            reason: "missing circuit id".to_string(),
2086        })?;
2087
2088        let circuit_id = if circuit_id_str == "0" {
2089            None
2090        } else {
2091            Some(CircuitId::new(circuit_id_str))
2092        };
2093
2094        let target = parts.next().ok_or_else(|| Error::Parse {
2095            location: "stream".to_string(),
2096            reason: "missing target".to_string(),
2097        })?;
2098
2099        let (target_host, target_port) = parse_target(target)?;
2100
2101        streams.push(Stream {
2102            id: StreamId::new(id),
2103            status,
2104            circuit_id,
2105            target_host,
2106            target_port,
2107        });
2108    }
2109
2110    Ok(streams)
2111}
2112
2113/// Parses a stream status string into a [`StreamStatus`] enum.
2114fn parse_stream_status(s: &str) -> Result<StreamStatus, Error> {
2115    match s.to_uppercase().as_str() {
2116        "NEW" => Ok(StreamStatus::New),
2117        "NEWRESOLVE" => Ok(StreamStatus::NewResolve),
2118        "REMAP" => Ok(StreamStatus::Remap),
2119        "SENTCONNECT" => Ok(StreamStatus::SentConnect),
2120        "SENTRESOLVE" => Ok(StreamStatus::SentResolve),
2121        "SUCCEEDED" => Ok(StreamStatus::Succeeded),
2122        "FAILED" => Ok(StreamStatus::Failed),
2123        "DETACHED" => Ok(StreamStatus::Detached),
2124        "CONTROLLER_WAIT" => Ok(StreamStatus::ControllerWait),
2125        "CLOSED" => Ok(StreamStatus::Closed),
2126        _ => Err(Error::Parse {
2127            location: "stream status".to_string(),
2128            reason: format!("unknown status: {}", s),
2129        }),
2130    }
2131}
2132
2133/// Parses a target address string into host and port components.
2134///
2135/// Handles formats like `host:port` or just `host` (port defaults to 0).
2136fn parse_target(target: &str) -> Result<(String, u16), Error> {
2137    if let Some(colon_pos) = target.rfind(':') {
2138        let host = target[..colon_pos].to_string();
2139        let port_str = &target[colon_pos + 1..];
2140        let port: u16 = port_str.parse().map_err(|_| Error::Parse {
2141            location: "stream target".to_string(),
2142            reason: format!("invalid port: {}", port_str),
2143        })?;
2144        Ok((host, port))
2145    } else {
2146        Ok((target.to_string(), 0))
2147    }
2148}
2149
2150#[cfg(test)]
2151mod tests {
2152    use super::*;
2153
2154    #[test]
2155    fn test_circuit_id_display() {
2156        let id = CircuitId::new("123");
2157        assert_eq!(id.to_string(), "123");
2158    }
2159
2160    #[test]
2161    fn test_stream_id_display() {
2162        let id = StreamId::new("456");
2163        assert_eq!(id.to_string(), "456");
2164    }
2165
2166    #[test]
2167    fn test_parse_circ_status() {
2168        assert_eq!(parse_circ_status("LAUNCHED").unwrap(), CircStatus::Launched);
2169        assert_eq!(parse_circ_status("BUILT").unwrap(), CircStatus::Built);
2170        assert_eq!(
2171            parse_circ_status("GUARD_WAIT").unwrap(),
2172            CircStatus::GuardWait
2173        );
2174        assert_eq!(parse_circ_status("EXTENDED").unwrap(), CircStatus::Extended);
2175        assert_eq!(parse_circ_status("FAILED").unwrap(), CircStatus::Failed);
2176        assert_eq!(parse_circ_status("CLOSED").unwrap(), CircStatus::Closed);
2177        assert_eq!(parse_circ_status("launched").unwrap(), CircStatus::Launched);
2178        assert!(parse_circ_status("UNKNOWN").is_err());
2179    }
2180
2181    #[test]
2182    fn test_parse_stream_status() {
2183        assert_eq!(parse_stream_status("NEW").unwrap(), StreamStatus::New);
2184        assert_eq!(
2185            parse_stream_status("NEWRESOLVE").unwrap(),
2186            StreamStatus::NewResolve
2187        );
2188        assert_eq!(parse_stream_status("REMAP").unwrap(), StreamStatus::Remap);
2189        assert_eq!(
2190            parse_stream_status("SENTCONNECT").unwrap(),
2191            StreamStatus::SentConnect
2192        );
2193        assert_eq!(
2194            parse_stream_status("SENTRESOLVE").unwrap(),
2195            StreamStatus::SentResolve
2196        );
2197        assert_eq!(
2198            parse_stream_status("SUCCEEDED").unwrap(),
2199            StreamStatus::Succeeded
2200        );
2201        assert_eq!(parse_stream_status("FAILED").unwrap(), StreamStatus::Failed);
2202        assert_eq!(
2203            parse_stream_status("DETACHED").unwrap(),
2204            StreamStatus::Detached
2205        );
2206        assert_eq!(
2207            parse_stream_status("CONTROLLER_WAIT").unwrap(),
2208            StreamStatus::ControllerWait
2209        );
2210        assert_eq!(parse_stream_status("CLOSED").unwrap(), StreamStatus::Closed);
2211        assert!(parse_stream_status("UNKNOWN").is_err());
2212    }
2213
2214    #[test]
2215    fn test_parse_relay_info_with_nickname() {
2216        let info = parse_relay_info("$ABCD1234~MyRelay");
2217        assert_eq!(info.fingerprint, "ABCD1234");
2218        assert_eq!(info.nickname, Some("MyRelay".to_string()));
2219    }
2220
2221    #[test]
2222    fn test_parse_relay_info_without_nickname() {
2223        let info = parse_relay_info("$ABCD1234");
2224        assert_eq!(info.fingerprint, "ABCD1234");
2225        assert_eq!(info.nickname, None);
2226    }
2227
2228    #[test]
2229    fn test_parse_relay_info_no_dollar() {
2230        let info = parse_relay_info("ABCD1234~MyRelay");
2231        assert_eq!(info.fingerprint, "ABCD1234");
2232        assert_eq!(info.nickname, Some("MyRelay".to_string()));
2233    }
2234
2235    #[test]
2236    fn test_parse_target_with_port() {
2237        let (host, port) = parse_target("example.com:443").unwrap();
2238        assert_eq!(host, "example.com");
2239        assert_eq!(port, 443);
2240    }
2241
2242    #[test]
2243    fn test_parse_target_ipv4_with_port() {
2244        let (host, port) = parse_target("192.168.1.1:80").unwrap();
2245        assert_eq!(host, "192.168.1.1");
2246        assert_eq!(port, 80);
2247    }
2248
2249    #[test]
2250    fn test_parse_target_without_port() {
2251        let (host, port) = parse_target("example.com").unwrap();
2252        assert_eq!(host, "example.com");
2253        assert_eq!(port, 0);
2254    }
2255
2256    #[test]
2257    fn test_parse_circuits_empty() {
2258        let circuits = parse_circuits("").unwrap();
2259        assert!(circuits.is_empty());
2260    }
2261
2262    #[test]
2263    fn test_parse_circuits_single() {
2264        let content = "1 BUILT $AAAA~Guard,$BBBB~Middle,$CCCC~Exit";
2265        let circuits = parse_circuits(content).unwrap();
2266        assert_eq!(circuits.len(), 1);
2267        assert_eq!(circuits[0].id.0, "1");
2268        assert_eq!(circuits[0].status, CircStatus::Built);
2269        assert_eq!(circuits[0].path.len(), 3);
2270        assert_eq!(circuits[0].path[0].fingerprint, "AAAA");
2271        assert_eq!(circuits[0].path[0].nickname, Some("Guard".to_string()));
2272    }
2273
2274    #[test]
2275    fn test_parse_circuits_multiple() {
2276        let content = "1 BUILT $AAAA~Guard,$BBBB~Exit\n2 LAUNCHED\n3 EXTENDED $CCCC~Relay";
2277        let circuits = parse_circuits(content).unwrap();
2278        assert_eq!(circuits.len(), 3);
2279        assert_eq!(circuits[0].status, CircStatus::Built);
2280        assert_eq!(circuits[1].status, CircStatus::Launched);
2281        assert_eq!(circuits[2].status, CircStatus::Extended);
2282    }
2283
2284    #[test]
2285    fn test_parse_circuits_with_flags() {
2286        let content = "1 BUILT $AAAA~Guard BUILD_FLAGS=IS_INTERNAL PURPOSE=GENERAL";
2287        let circuits = parse_circuits(content).unwrap();
2288        assert_eq!(circuits.len(), 1);
2289        assert_eq!(circuits[0].path.len(), 1);
2290    }
2291
2292    #[test]
2293    fn test_parse_streams_empty() {
2294        let streams = parse_streams("").unwrap();
2295        assert!(streams.is_empty());
2296    }
2297
2298    #[test]
2299    fn test_parse_streams_single() {
2300        let content = "1 SUCCEEDED 5 www.example.com:443";
2301        let streams = parse_streams(content).unwrap();
2302        assert_eq!(streams.len(), 1);
2303        assert_eq!(streams[0].id.0, "1");
2304        assert_eq!(streams[0].status, StreamStatus::Succeeded);
2305        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("5")));
2306        assert_eq!(streams[0].target_host, "www.example.com");
2307        assert_eq!(streams[0].target_port, 443);
2308    }
2309
2310    #[test]
2311    fn test_parse_streams_no_circuit() {
2312        let content = "1 NEW 0 www.example.com:80";
2313        let streams = parse_streams(content).unwrap();
2314        assert_eq!(streams.len(), 1);
2315        assert_eq!(streams[0].circuit_id, None);
2316    }
2317
2318    #[test]
2319    fn test_parse_streams_multiple() {
2320        let content = "1 SUCCEEDED 5 www.example.com:443\n2 NEW 0 api.example.com:80";
2321        let streams = parse_streams(content).unwrap();
2322        assert_eq!(streams.len(), 2);
2323    }
2324
2325    #[test]
2326    fn test_circuit_id_equality() {
2327        let id1 = CircuitId::new("123");
2328        let id2 = CircuitId::new("123");
2329        let id3 = CircuitId::new("456");
2330        assert_eq!(id1, id2);
2331        assert_ne!(id1, id3);
2332    }
2333
2334    #[test]
2335    fn test_stream_id_equality() {
2336        let id1 = StreamId::new("123");
2337        let id2 = StreamId::new("123");
2338        let id3 = StreamId::new("456");
2339        assert_eq!(id1, id2);
2340        assert_ne!(id1, id3);
2341    }
2342}
2343
2344#[cfg(test)]
2345mod stem_tests {
2346    use super::*;
2347
2348    #[test]
2349    fn test_parse_circ_path_empty() {
2350        let circuits = parse_circuits("").unwrap();
2351        assert!(circuits.is_empty());
2352    }
2353
2354    #[test]
2355    fn test_parse_circ_path_with_fingerprint_and_nickname() {
2356        let content = "1 BUILT $999A226EBED397F331B612FE1E4CFAE5C1F201BA~piyaz";
2357        let circuits = parse_circuits(content).unwrap();
2358        assert_eq!(circuits.len(), 1);
2359        assert_eq!(circuits[0].path.len(), 1);
2360        assert_eq!(
2361            circuits[0].path[0].fingerprint,
2362            "999A226EBED397F331B612FE1E4CFAE5C1F201BA"
2363        );
2364        assert_eq!(circuits[0].path[0].nickname, Some("piyaz".to_string()));
2365    }
2366
2367    #[test]
2368    fn test_parse_circ_path_multiple_relays() {
2369        let content =
2370            "1 BUILT $E57A476CD4DFBD99B4EE52A100A58610AD6E80B9,$AAAA,$BBBB~PrivacyRepublic14";
2371        let circuits = parse_circuits(content).unwrap();
2372        assert_eq!(circuits.len(), 1);
2373        assert_eq!(circuits[0].path.len(), 3);
2374        assert_eq!(
2375            circuits[0].path[0].fingerprint,
2376            "E57A476CD4DFBD99B4EE52A100A58610AD6E80B9"
2377        );
2378        assert_eq!(circuits[0].path[0].nickname, None);
2379        assert_eq!(circuits[0].path[2].fingerprint, "BBBB");
2380        assert_eq!(
2381            circuits[0].path[2].nickname,
2382            Some("PrivacyRepublic14".to_string())
2383        );
2384    }
2385
2386    #[test]
2387    fn test_get_streams_parsing() {
2388        let content =
2389            "1 NEW 4 10.10.10.1:80\n2 SUCCEEDED 4 10.10.10.1:80\n3 SUCCEEDED 4 10.10.10.1:80";
2390        let streams = parse_streams(content).unwrap();
2391        assert_eq!(streams.len(), 3);
2392
2393        assert_eq!(streams[0].id.0, "1");
2394        assert_eq!(streams[0].status, StreamStatus::New);
2395        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("4")));
2396        assert_eq!(streams[0].target_host, "10.10.10.1");
2397        assert_eq!(streams[0].target_port, 80);
2398
2399        assert_eq!(streams[1].id.0, "2");
2400        assert_eq!(streams[1].status, StreamStatus::Succeeded);
2401
2402        assert_eq!(streams[2].id.0, "3");
2403        assert_eq!(streams[2].status, StreamStatus::Succeeded);
2404    }
2405
2406    #[test]
2407    fn test_circuit_status_parsing() {
2408        let test_cases = [
2409            ("LAUNCHED", CircStatus::Launched),
2410            ("BUILT", CircStatus::Built),
2411            ("GUARD_WAIT", CircStatus::GuardWait),
2412            ("EXTENDED", CircStatus::Extended),
2413            ("FAILED", CircStatus::Failed),
2414            ("CLOSED", CircStatus::Closed),
2415        ];
2416
2417        for (input, expected) in test_cases {
2418            assert_eq!(parse_circ_status(input).unwrap(), expected);
2419        }
2420    }
2421
2422    #[test]
2423    fn test_stream_status_parsing() {
2424        let test_cases = [
2425            ("NEW", StreamStatus::New),
2426            ("NEWRESOLVE", StreamStatus::NewResolve),
2427            ("REMAP", StreamStatus::Remap),
2428            ("SENTCONNECT", StreamStatus::SentConnect),
2429            ("SENTRESOLVE", StreamStatus::SentResolve),
2430            ("SUCCEEDED", StreamStatus::Succeeded),
2431            ("FAILED", StreamStatus::Failed),
2432            ("DETACHED", StreamStatus::Detached),
2433            ("CONTROLLER_WAIT", StreamStatus::ControllerWait),
2434            ("CLOSED", StreamStatus::Closed),
2435        ];
2436
2437        for (input, expected) in test_cases {
2438            assert_eq!(parse_stream_status(input).unwrap(), expected);
2439        }
2440    }
2441
2442    #[test]
2443    fn test_parse_target_various() {
2444        let test_cases = [
2445            ("www.example.com:443", ("www.example.com", 443)),
2446            ("192.168.1.1:80", ("192.168.1.1", 80)),
2447            ("10.10.10.1:8080", ("10.10.10.1", 8080)),
2448            ("[::1]:443", ("[::1]", 443)),
2449        ];
2450
2451        for (input, (expected_host, expected_port)) in test_cases {
2452            let (host, port) = parse_target(input).unwrap();
2453            assert_eq!(host, expected_host);
2454            assert_eq!(port, expected_port);
2455        }
2456    }
2457
2458    #[test]
2459    fn test_parse_circuits_with_build_flags() {
2460        let content = "1 BUILT $AAAA~Guard,$BBBB~Exit BUILD_FLAGS=IS_INTERNAL,NEED_CAPACITY PURPOSE=GENERAL TIME_CREATED=2023-01-01T00:00:00";
2461        let circuits = parse_circuits(content).unwrap();
2462        assert_eq!(circuits.len(), 1);
2463        assert_eq!(circuits[0].status, CircStatus::Built);
2464        assert_eq!(circuits[0].path.len(), 2);
2465    }
2466
2467    #[test]
2468    fn test_parse_circuits_launched_no_path() {
2469        let content = "1 LAUNCHED BUILD_FLAGS=NEED_CAPACITY PURPOSE=GENERAL";
2470        let circuits = parse_circuits(content).unwrap();
2471        assert_eq!(circuits.len(), 1);
2472        assert_eq!(circuits[0].status, CircStatus::Launched);
2473        assert!(circuits[0].path.is_empty());
2474    }
2475
2476    #[test]
2477    fn test_parse_streams_detached() {
2478        let content = "1 DETACHED 0 www.example.com:443";
2479        let streams = parse_streams(content).unwrap();
2480        assert_eq!(streams.len(), 1);
2481        assert_eq!(streams[0].status, StreamStatus::Detached);
2482        assert_eq!(streams[0].circuit_id, None);
2483    }
2484
2485    #[test]
2486    fn test_relay_info_parsing_variations() {
2487        let test_cases = [
2488            ("$ABCD1234~MyRelay", "ABCD1234", Some("MyRelay")),
2489            ("$ABCD1234", "ABCD1234", None),
2490            ("ABCD1234~MyRelay", "ABCD1234", Some("MyRelay")),
2491            ("ABCD1234", "ABCD1234", None),
2492        ];
2493
2494        for (input, expected_fp, expected_nick) in test_cases {
2495            let info = parse_relay_info(input);
2496            assert_eq!(info.fingerprint, expected_fp);
2497            assert_eq!(info.nickname, expected_nick.map(|s| s.to_string()));
2498        }
2499    }
2500
2501    #[test]
2502    fn test_circuit_id_hash() {
2503        use std::collections::HashSet;
2504        let mut set = HashSet::new();
2505        set.insert(CircuitId::new("1"));
2506        set.insert(CircuitId::new("2"));
2507        set.insert(CircuitId::new("1"));
2508        assert_eq!(set.len(), 2);
2509    }
2510
2511    #[test]
2512    fn test_stream_id_hash() {
2513        use std::collections::HashSet;
2514        let mut set = HashSet::new();
2515        set.insert(StreamId::new("1"));
2516        set.insert(StreamId::new("2"));
2517        set.insert(StreamId::new("1"));
2518        assert_eq!(set.len(), 2);
2519    }
2520
2521    #[test]
2522    fn test_parse_circuits_real_world_example() {
2523        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"#;
2524        let circuits = parse_circuits(content).unwrap();
2525        assert_eq!(circuits.len(), 1);
2526        assert_eq!(circuits[0].id.0, "7");
2527        assert_eq!(circuits[0].status, CircStatus::Built);
2528        assert_eq!(circuits[0].path.len(), 3);
2529        assert_eq!(
2530            circuits[0].path[0].nickname,
2531            Some("Quetzalcoatl".to_string())
2532        );
2533        assert_eq!(
2534            circuits[0].path[1].nickname,
2535            Some("DigiGesTor4e3".to_string())
2536        );
2537        assert_eq!(
2538            circuits[0].path[2].nickname,
2539            Some("torserversNet".to_string())
2540        );
2541    }
2542
2543    #[test]
2544    fn test_parse_streams_real_world_example() {
2545        let content =
2546            "42 SUCCEEDED 7 www.torproject.org:443 SOURCE_ADDR=127.0.0.1:12345 PURPOSE=USER";
2547        let streams = parse_streams(content).unwrap();
2548        assert_eq!(streams.len(), 1);
2549        assert_eq!(streams[0].id.0, "42");
2550        assert_eq!(streams[0].status, StreamStatus::Succeeded);
2551        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("7")));
2552        assert_eq!(streams[0].target_host, "www.torproject.org");
2553        assert_eq!(streams[0].target_port, 443);
2554    }
2555
2556    #[test]
2557    fn test_parse_add_onion_response_v3() {
2558        let content = "ServiceID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nPrivateKey=ED25519-V3:base64keydata==";
2559        let response = parse_add_onion_response(content).unwrap();
2560        assert_eq!(response.service_id, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
2561        assert_eq!(response.private_key_type, Some("ED25519-V3".to_string()));
2562        assert_eq!(response.private_key, Some("base64keydata==".to_string()));
2563    }
2564
2565    #[test]
2566    fn test_parse_add_onion_response_discarded_key() {
2567        let content = "ServiceID=abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv";
2568        let response = parse_add_onion_response(content).unwrap();
2569        assert_eq!(response.service_id, "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv");
2570        assert!(response.private_key.is_none());
2571        assert!(response.private_key_type.is_none());
2572    }
2573
2574    #[test]
2575    fn test_parse_add_onion_response_missing_service_id() {
2576        let content = "PrivateKey=ED25519-V3:base64keydata==";
2577        let result = parse_add_onion_response(content);
2578        assert!(result.is_err());
2579    }
2580}