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`]
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`]: 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, HashSet};
115use std::net::SocketAddr;
116use std::path::Path;
117use std::time::{SystemTime, UNIX_EPOCH};
118
119use crate::auth;
120use crate::events::ParsedEvent;
121use crate::protocol::ControlLine;
122use crate::socket::{ControlMessage, ControlSocket};
123use crate::version::Version;
124use crate::{CircStatus, Error, EventType, Signal, StreamStatus};
125
126/// Types of listeners that Tor can have.
127///
128/// These correspond to the different types of connections that Tor handles,
129/// each configured via different torrc options.
130///
131/// # Example
132///
133/// ```rust
134/// use stem_rs::controller::ListenerType;
135///
136/// let listener = ListenerType::Socks;
137/// assert_eq!(listener.to_string(), "socks");
138/// ```
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140pub enum ListenerType {
141    /// Traffic we're relaying as a member of the network (ORPort)
142    Or,
143    /// Mirroring for tor descriptor content (DirPort)
144    Dir,
145    /// Client traffic we're sending over Tor (SocksPort)
146    Socks,
147    /// Transparent proxy handling (TransPort)
148    Trans,
149    /// Forwarding for ipfw NATD connections (NatdPort)
150    Natd,
151    /// DNS lookups for our traffic (DNSPort)
152    Dns,
153    /// Controller applications (ControlPort)
154    Control,
155    /// Pluggable transport for Extended ORPorts (ExtORPort)
156    ExtOr,
157    /// HTTP tunneling proxy (HTTPTunnelPort)
158    HttpTunnel,
159}
160
161impl std::fmt::Display for ListenerType {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        match self {
164            ListenerType::Or => write!(f, "or"),
165            ListenerType::Dir => write!(f, "dir"),
166            ListenerType::Socks => write!(f, "socks"),
167            ListenerType::Trans => write!(f, "trans"),
168            ListenerType::Natd => write!(f, "natd"),
169            ListenerType::Dns => write!(f, "dns"),
170            ListenerType::Control => write!(f, "control"),
171            ListenerType::ExtOr => write!(f, "extor"),
172            ListenerType::HttpTunnel => write!(f, "httptunnel"),
173        }
174    }
175}
176
177/// Purpose for a circuit.
178///
179/// Circuits can be created for different purposes, which affects how Tor
180/// uses them.
181///
182/// # Example
183///
184/// ```rust
185/// use stem_rs::controller::CircuitPurpose;
186///
187/// let purpose = CircuitPurpose::General;
188/// assert_eq!(purpose.to_string(), "general");
189/// ```
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191pub enum CircuitPurpose {
192    /// General purpose circuit for normal traffic
193    General,
194    /// Circuit created and managed by a controller
195    Controller,
196}
197
198impl std::fmt::Display for CircuitPurpose {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        match self {
201            CircuitPurpose::General => write!(f, "general"),
202            CircuitPurpose::Controller => write!(f, "controller"),
203        }
204    }
205}
206
207/// Protocol information returned by PROTOCOLINFO command.
208///
209/// Contains information about the Tor control protocol version,
210/// the Tor version, and available authentication methods.
211///
212/// # Example
213///
214/// ```rust,no_run
215/// use stem_rs::controller::Controller;
216///
217/// # async fn example() -> Result<(), stem_rs::Error> {
218/// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
219/// let info = controller.get_protocolinfo().await?;
220/// println!("Tor version: {}", info.tor_version);
221/// println!("Auth methods: {:?}", info.auth_methods);
222/// # Ok(())
223/// # }
224/// ```
225#[derive(Debug, Clone)]
226pub struct ProtocolInfo {
227    /// Protocol version (typically 1)
228    pub protocol_version: u32,
229    /// Tor version string
230    pub tor_version: String,
231    /// Available authentication methods
232    pub auth_methods: Vec<String>,
233    /// Path to cookie file (if cookie auth available)
234    pub cookie_file: Option<String>,
235}
236
237/// Accounting statistics for bandwidth limiting.
238///
239/// Contains information about Tor's accounting status when AccountingMax
240/// is set in the torrc. This includes read/write limits and current usage.
241///
242/// # Example
243///
244/// ```rust,no_run
245/// use stem_rs::controller::Controller;
246///
247/// # async fn example() -> Result<(), stem_rs::Error> {
248/// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
249/// controller.authenticate(None).await?;
250/// let stats = controller.get_accounting_stats().await?;
251/// println!("Status: {}", stats.status);
252/// println!("Read: {} bytes", stats.read_bytes);
253/// # Ok(())
254/// # }
255/// ```
256#[derive(Debug, Clone)]
257pub struct AccountingStats {
258    /// Unix timestamp when this was retrieved
259    pub retrieved: f64,
260    /// Hibernation status: "awake", "soft", or "hard"
261    pub status: String,
262    /// Time when accounting interval ends (ISO format string)
263    pub interval_end: Option<String>,
264    /// Seconds until limits reset
265    pub time_until_reset: u64,
266    /// Bytes read during this interval
267    pub read_bytes: u64,
268    /// Bytes remaining before read limit
269    pub read_bytes_left: u64,
270    /// Read limit in bytes
271    pub read_limit: u64,
272    /// Bytes written during this interval
273    pub written_bytes: u64,
274    /// Bytes remaining before write limit
275    pub write_bytes_left: u64,
276    /// Write limit in bytes
277    pub write_limit: u64,
278}
279
280/// A unique identifier for a Tor circuit.
281///
282/// Circuit IDs are assigned by Tor when circuits are created and are used
283/// to reference specific circuits in control protocol commands. The ID is
284/// a string representation of a numeric identifier.
285///
286/// # Invariants
287///
288/// - Circuit IDs are unique within a Tor session
289/// - IDs are assigned sequentially by Tor
290/// - An ID remains valid until the circuit is closed
291///
292/// # Example
293///
294/// ```rust
295/// use stem_rs::controller::CircuitId;
296///
297/// let id = CircuitId::new("42");
298/// assert_eq!(id.to_string(), "42");
299///
300/// // CircuitIds can be compared for equality
301/// let id2 = CircuitId::new("42");
302/// assert_eq!(id, id2);
303/// ```
304///
305/// # See Also
306///
307/// - [`Controller::get_circuits`]: Retrieve active circuits
308/// - [`Controller::new_circuit`]: Create a new circuit
309/// - [`Controller::close_circuit`]: Close a circuit by ID
310#[derive(Debug, Clone, PartialEq, Eq, Hash)]
311pub struct CircuitId(pub String);
312
313impl CircuitId {
314    /// Creates a new circuit ID from any string-like value.
315    ///
316    /// # Arguments
317    ///
318    /// * `id` - The circuit identifier, typically a numeric string
319    ///
320    /// # Example
321    ///
322    /// ```rust
323    /// use stem_rs::controller::CircuitId;
324    ///
325    /// let id = CircuitId::new("123");
326    /// let id_from_string = CircuitId::new(String::from("123"));
327    /// assert_eq!(id, id_from_string);
328    /// ```
329    pub fn new(id: impl Into<String>) -> Self {
330        Self(id.into())
331    }
332}
333
334impl std::fmt::Display for CircuitId {
335    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
336        write!(f, "{}", self.0)
337    }
338}
339
340/// A unique identifier for a Tor stream.
341///
342/// Stream IDs are assigned by Tor when streams are created and are used
343/// to reference specific streams in control protocol commands. Streams
344/// represent individual TCP connections being routed through Tor circuits.
345///
346/// # Invariants
347///
348/// - Stream IDs are unique within a Tor session
349/// - IDs are assigned sequentially by Tor
350/// - An ID remains valid until the stream is closed
351///
352/// # Example
353///
354/// ```rust
355/// use stem_rs::controller::StreamId;
356///
357/// let id = StreamId::new("99");
358/// assert_eq!(id.to_string(), "99");
359///
360/// // StreamIds can be compared for equality
361/// let id2 = StreamId::new("99");
362/// assert_eq!(id, id2);
363/// ```
364///
365/// # See Also
366///
367/// - [`Controller::get_streams`]: Retrieve active streams
368/// - [`Controller::attach_stream`]: Attach a stream to a circuit
369/// - [`Controller::close_stream`]: Close a stream by ID
370#[derive(Debug, Clone, PartialEq, Eq, Hash)]
371pub struct StreamId(pub String);
372
373impl StreamId {
374    /// Creates a new stream ID from any string-like value.
375    ///
376    /// # Arguments
377    ///
378    /// * `id` - The stream identifier, typically a numeric string
379    ///
380    /// # Example
381    ///
382    /// ```rust
383    /// use stem_rs::controller::StreamId;
384    ///
385    /// let id = StreamId::new("456");
386    /// let id_from_string = StreamId::new(String::from("456"));
387    /// assert_eq!(id, id_from_string);
388    /// ```
389    pub fn new(id: impl Into<String>) -> Self {
390        Self(id.into())
391    }
392}
393
394impl std::fmt::Display for StreamId {
395    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
396        write!(f, "{}", self.0)
397    }
398}
399
400/// Information about a relay in a circuit path.
401///
402/// Each hop in a Tor circuit is represented by a `RelayInfo` containing
403/// the relay's fingerprint and optionally its nickname. The fingerprint
404/// is a 40-character hexadecimal string representing the SHA-1 hash of
405/// the relay's identity key.
406///
407/// # Fields
408///
409/// - `fingerprint`: The relay's identity fingerprint (40 hex characters)
410/// - `nickname`: The relay's optional human-readable nickname
411///
412/// # Example
413///
414/// ```rust
415/// use stem_rs::controller::RelayInfo;
416///
417/// let relay = RelayInfo {
418///     fingerprint: "9695DFC35FFEB861329B9F1AB04C46397020CE31".to_string(),
419///     nickname: Some("MyRelay".to_string()),
420/// };
421///
422/// println!("Relay: {} ({:?})", relay.fingerprint, relay.nickname);
423/// ```
424///
425/// # See Also
426///
427/// - [`Circuit`]: Contains a path of `RelayInfo` entries
428/// - [`util::is_valid_fingerprint`](crate::util::is_valid_fingerprint): Validate fingerprint format
429#[derive(Debug, Clone)]
430pub struct RelayInfo {
431    /// The relay's identity fingerprint (40 hexadecimal characters).
432    ///
433    /// This is the SHA-1 hash of the relay's identity key, used to uniquely
434    /// identify relays across the Tor network.
435    pub fingerprint: String,
436
437    /// The relay's optional human-readable nickname.
438    ///
439    /// Nicknames are chosen by relay operators and are not guaranteed to be
440    /// unique. May be `None` if the nickname was not provided in the circuit
441    /// status response.
442    pub nickname: Option<String>,
443}
444
445/// Information about an active Tor circuit.
446///
447/// A circuit is a path through the Tor network consisting of multiple
448/// relay hops. Circuits are used to route traffic anonymously by encrypting
449/// data in layers that are peeled off at each hop.
450///
451/// # Circuit Lifecycle
452///
453/// Circuits progress through several states:
454///
455/// 1. **Launched**: Circuit creation has begun
456/// 2. **Extended**: Circuit is being extended to additional hops
457/// 3. **Built**: Circuit is fully constructed and ready for use
458/// 4. **Failed**: Circuit construction failed
459/// 5. **Closed**: Circuit has been closed
460///
461/// # Fields
462///
463/// - `id`: Unique identifier for this circuit
464/// - `status`: Current state of the circuit
465/// - `path`: Ordered list of relays in the circuit (guard → middle → exit)
466///
467/// # Example
468///
469/// ```rust,no_run
470/// use stem_rs::controller::Controller;
471/// use stem_rs::CircStatus;
472///
473/// # async fn example() -> Result<(), stem_rs::Error> {
474/// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
475/// controller.authenticate(None).await?;
476///
477/// for circuit in controller.get_circuits().await? {
478///     if circuit.status == CircStatus::Built {
479///         println!("Circuit {} has {} hops:", circuit.id, circuit.path.len());
480///         for (i, relay) in circuit.path.iter().enumerate() {
481///             println!("  Hop {}: {} ({:?})", i + 1, relay.fingerprint, relay.nickname);
482///         }
483///     }
484/// }
485/// # Ok(())
486/// # }
487/// ```
488///
489/// # See Also
490///
491/// - [`Controller::get_circuits`]: Retrieve all active circuits
492/// - [`Controller::new_circuit`]: Create a new circuit
493/// - [`CircStatus`]: Circuit status enumeration
494#[derive(Debug, Clone)]
495pub struct Circuit {
496    /// Unique identifier for this circuit.
497    pub id: CircuitId,
498
499    /// Current status of the circuit.
500    ///
501    /// See [`CircStatus`] for possible values.
502    pub status: CircStatus,
503
504    /// Ordered list of relays in the circuit path.
505    ///
506    /// The first relay is the guard (entry) node, and the last relay is
507    /// typically the exit node. The path may be empty for newly launched
508    /// circuits that haven't yet established any hops.
509    pub path: Vec<RelayInfo>,
510}
511
512/// Information about an active Tor stream.
513///
514/// A stream represents a single TCP connection being routed through a Tor
515/// circuit. Streams are created when applications connect through Tor's
516/// SOCKS proxy and are attached to circuits for routing.
517///
518/// # Stream Lifecycle
519///
520/// Streams progress through several states:
521///
522/// 1. **New**: Stream created, awaiting circuit attachment
523/// 2. **SentConnect**: CONNECT command sent to exit relay
524/// 3. **Succeeded**: Connection established successfully
525/// 4. **Failed**: Connection attempt failed
526/// 5. **Closed**: Stream has been closed
527///
528/// # Fields
529///
530/// - `id`: Unique identifier for this stream
531/// - `status`: Current state of the stream
532/// - `circuit_id`: The circuit this stream is attached to (if any)
533/// - `target_host`: Destination hostname or IP address
534/// - `target_port`: Destination port number
535///
536/// # Example
537///
538/// ```rust,no_run
539/// use stem_rs::controller::Controller;
540/// use stem_rs::StreamStatus;
541///
542/// # async fn example() -> Result<(), stem_rs::Error> {
543/// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
544/// controller.authenticate(None).await?;
545///
546/// for stream in controller.get_streams().await? {
547///     println!("Stream {} -> {}:{} ({:?})",
548///         stream.id,
549///         stream.target_host,
550///         stream.target_port,
551///         stream.status
552///     );
553///     if let Some(ref circuit_id) = stream.circuit_id {
554///         println!("  Attached to circuit {}", circuit_id);
555///     }
556/// }
557/// # Ok(())
558/// # }
559/// ```
560///
561/// # See Also
562///
563/// - [`Controller::get_streams`]: Retrieve all active streams
564/// - [`Controller::attach_stream`]: Attach a stream to a circuit
565/// - [`StreamStatus`]: Stream status enumeration
566#[derive(Debug, Clone)]
567pub struct Stream {
568    /// Unique identifier for this stream.
569    pub id: StreamId,
570
571    /// Current status of the stream.
572    ///
573    /// See [`StreamStatus`] for possible values.
574    pub status: StreamStatus,
575
576    /// The circuit this stream is attached to, if any.
577    ///
578    /// Streams in the `New` or `Detached` state may not be attached to
579    /// any circuit. Once attached, this field contains the circuit ID.
580    pub circuit_id: Option<CircuitId>,
581
582    /// Destination hostname or IP address.
583    ///
584    /// This is the target the stream is connecting to through Tor.
585    pub target_host: String,
586
587    /// Destination port number.
588    ///
589    /// The TCP port on the target host. May be 0 if not specified.
590    pub target_port: u16,
591}
592
593/// A high-level interface for interacting with Tor's control protocol.
594///
595/// The `Controller` provides the primary API for controlling a Tor process.
596/// It wraps a [`ControlSocket`] and provides
597/// typed methods for common operations like authentication, circuit management,
598/// and event subscription.
599///
600/// # Conceptual Role
601///
602/// The Controller is the main entry point for most stem-rs users. It handles:
603///
604/// - Protocol message formatting and parsing
605/// - Response validation and error handling
606/// - Asynchronous event buffering
607/// - Connection lifecycle management
608///
609/// # What This Type Does NOT Do
610///
611/// - Direct relay communication (use [`client::Relay`](crate::client::Relay))
612/// - Descriptor parsing (use [`descriptor`](crate::descriptor) module)
613/// - Exit policy evaluation (use [`ExitPolicy`](crate::exit_policy::ExitPolicy))
614///
615/// # Invariants
616///
617/// - The underlying socket connection is valid while the Controller exists
618/// - After successful authentication, the controller is ready for commands
619/// - Events received during command execution are buffered for later retrieval
620///
621/// # Thread Safety
622///
623/// `Controller` is `Send` but not `Sync`. For concurrent access from multiple
624/// tasks, wrap in `Arc<Mutex<Controller>>`:
625///
626/// ```rust,no_run
627/// use std::sync::Arc;
628/// use tokio::sync::Mutex;
629/// use stem_rs::controller::Controller;
630///
631/// # async fn example() -> Result<(), stem_rs::Error> {
632/// let controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
633/// let shared = Arc::new(Mutex::new(controller));
634///
635/// // Clone Arc for each task
636/// let c1 = shared.clone();
637/// tokio::spawn(async move {
638///     let mut ctrl = c1.lock().await;
639///     // Use controller...
640/// });
641/// # Ok(())
642/// # }
643/// ```
644///
645/// # Example
646///
647/// ```rust,no_run
648/// use stem_rs::controller::Controller;
649/// use stem_rs::Signal;
650///
651/// # async fn example() -> Result<(), stem_rs::Error> {
652/// // Connect and authenticate
653/// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
654/// controller.authenticate(Some("my_password")).await?;
655///
656/// // Query information
657/// let version = controller.get_version().await?;
658/// let circuits = controller.get_circuits().await?;
659///
660/// // Send signal
661/// controller.signal(Signal::Newnym).await?;
662/// # Ok(())
663/// # }
664/// ```
665///
666/// # Security
667///
668/// - Passwords are not stored after authentication
669/// - Cookie files are read with minimal permissions
670/// - SAFECOOKIE uses secure random nonces
671///
672/// # See Also
673///
674/// - [`from_port`](Controller::from_port): Connect via TCP
675/// - [`from_socket_file`](Controller::from_socket_file): Connect via Unix socket
676/// - [`authenticate`](Controller::authenticate): Authenticate with Tor
677pub struct Controller {
678    /// The underlying control socket connection.
679    socket: ControlSocket,
680    /// Buffer for asynchronous events received during command execution.
681    event_buffer: Vec<ControlMessage>,
682    /// Optional descriptor cache for improved performance.
683    #[cfg(feature = "descriptors")]
684    descriptor_cache: Option<crate::descriptor::DescriptorCache>,
685}
686
687impl Controller {
688    /// Creates a new Controller connected to a TCP control port.
689    ///
690    /// Establishes a TCP connection to Tor's control port at the specified
691    /// address. The connection is unauthenticated; call [`authenticate`](Self::authenticate)
692    /// before issuing commands.
693    ///
694    /// # Arguments
695    ///
696    /// * `addr` - The socket address of Tor's control port (e.g., `127.0.0.1:9051`)
697    ///
698    /// # Errors
699    ///
700    /// Returns [`Error::Socket`] if:
701    /// - The connection is refused (Tor not running or port incorrect)
702    /// - Network is unreachable
703    /// - Connection times out
704    ///
705    /// # Example
706    ///
707    /// ```rust,no_run
708    /// use stem_rs::controller::Controller;
709    ///
710    /// # async fn example() -> Result<(), stem_rs::Error> {
711    /// // Connect to default control port
712    /// let controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
713    ///
714    /// // Connect to custom port
715    /// let controller = Controller::from_port("127.0.0.1:9151".parse()?).await?;
716    /// # Ok(())
717    /// # }
718    /// ```
719    ///
720    /// # See Also
721    ///
722    /// - [`from_socket_file`](Self::from_socket_file): Connect via Unix socket
723    /// - [`authenticate`](Self::authenticate): Authenticate after connecting
724    pub async fn from_port(addr: SocketAddr) -> Result<Self, Error> {
725        let socket = ControlSocket::connect_port(addr).await?;
726        Ok(Self {
727            socket,
728            event_buffer: Vec::new(),
729            #[cfg(feature = "descriptors")]
730            descriptor_cache: None,
731        })
732    }
733
734    /// Creates a new Controller connected to a Unix domain socket.
735    ///
736    /// Establishes a connection to Tor's control socket at the specified
737    /// file path. This is typically more secure than TCP as it doesn't
738    /// expose the control interface to the network.
739    ///
740    /// # Arguments
741    ///
742    /// * `path` - Path to Tor's control socket file (e.g., `/var/run/tor/control`)
743    ///
744    /// # Errors
745    ///
746    /// Returns [`Error::Socket`] if:
747    /// - The socket file doesn't exist
748    /// - Permission denied accessing the socket
749    /// - The socket is not a valid Unix domain socket
750    ///
751    /// # Example
752    ///
753    /// ```rust,no_run
754    /// use std::path::Path;
755    /// use stem_rs::controller::Controller;
756    ///
757    /// # async fn example() -> Result<(), stem_rs::Error> {
758    /// // Connect to Tor's Unix control socket
759    /// let controller = Controller::from_socket_file(
760    ///     Path::new("/var/run/tor/control")
761    /// ).await?;
762    /// # Ok(())
763    /// # }
764    /// ```
765    ///
766    /// # Platform Support
767    ///
768    /// Unix domain sockets are only available on Unix-like systems (Linux, macOS, BSD).
769    /// On Windows, use [`from_port`](Self::from_port) instead.
770    ///
771    /// # See Also
772    ///
773    /// - [`from_port`](Self::from_port): Connect via TCP
774    /// - [`authenticate`](Self::authenticate): Authenticate after connecting
775    pub async fn from_socket_file(path: &Path) -> Result<Self, Error> {
776        let socket = ControlSocket::connect_unix(path).await?;
777        Ok(Self {
778            socket,
779            event_buffer: Vec::new(),
780            #[cfg(feature = "descriptors")]
781            descriptor_cache: None,
782        })
783    }
784
785    /// Enables descriptor caching with default settings.
786    ///
787    /// Caching significantly improves performance by avoiding repeated downloads
788    /// of the same descriptors. The cache uses sensible defaults:
789    /// - Consensus: 3 hours TTL
790    /// - Server descriptors: 24 hours TTL
791    /// - Microdescriptors: 24 hours TTL
792    /// - Max entries: 1000
793    ///
794    /// # Example
795    ///
796    /// ```rust,no_run
797    /// use stem_rs::controller::Controller;
798    ///
799    /// # async fn example() -> Result<(), stem_rs::Error> {
800    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
801    /// controller.authenticate(None).await?;
802    ///
803    /// // Enable caching for better performance
804    /// controller.enable_descriptor_cache();
805    ///
806    /// // Subsequent calls will use cached data when available
807    /// let consensus = controller.get_consensus().await?;
808    /// # Ok(())
809    /// # }
810    /// ```
811    #[cfg(feature = "descriptors")]
812    pub fn enable_descriptor_cache(&mut self) {
813        self.descriptor_cache = Some(crate::descriptor::DescriptorCache::new());
814    }
815
816    /// Enables descriptor caching with a custom cache instance.
817    ///
818    /// Allows fine-grained control over cache behavior including TTLs
819    /// and maximum entry limits.
820    ///
821    /// # Arguments
822    ///
823    /// * `cache` - A configured [`DescriptorCache`](crate::descriptor::DescriptorCache) instance
824    ///
825    /// # Example
826    ///
827    /// ```rust,no_run
828    /// use stem_rs::controller::Controller;
829    /// use stem_rs::descriptor::DescriptorCache;
830    /// use std::time::Duration;
831    ///
832    /// # async fn example() -> Result<(), stem_rs::Error> {
833    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
834    /// controller.authenticate(None).await?;
835    ///
836    /// // Configure custom cache settings
837    /// let cache = DescriptorCache::new()
838    ///     .with_consensus_ttl(Duration::from_secs(1800))  // 30 minutes
839    ///     .with_max_entries(500);
840    ///
841    /// controller.with_descriptor_cache(cache);
842    /// # Ok(())
843    /// # }
844    /// ```
845    #[cfg(feature = "descriptors")]
846    pub fn with_descriptor_cache(&mut self, cache: crate::descriptor::DescriptorCache) {
847        self.descriptor_cache = Some(cache);
848    }
849
850    /// Disables descriptor caching.
851    ///
852    /// Clears any cached descriptors and disables future caching.
853    /// All subsequent descriptor requests will fetch fresh data from Tor.
854    #[cfg(feature = "descriptors")]
855    pub fn disable_descriptor_cache(&mut self) {
856        self.descriptor_cache = None;
857    }
858
859    /// Returns cache statistics if caching is enabled.
860    ///
861    /// Provides insight into cache performance including hit rate,
862    /// number of evictions, and expiration counts.
863    ///
864    /// # Returns
865    ///
866    /// Returns `Some(CacheStats)` if caching is enabled, `None` otherwise.
867    ///
868    /// # Example
869    ///
870    /// ```rust,no_run
871    /// use stem_rs::controller::Controller;
872    ///
873    /// # async fn example() -> Result<(), stem_rs::Error> {
874    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
875    /// controller.authenticate(None).await?;
876    /// controller.enable_descriptor_cache();
877    ///
878    /// // Make some descriptor requests...
879    /// let _ = controller.get_consensus().await?;
880    ///
881    /// // Check cache performance
882    /// if let Some(stats) = controller.cache_stats() {
883    ///     println!("Cache hit rate: {:.1}%", stats.hit_rate());
884    ///     println!("Hits: {}, Misses: {}", stats.hits, stats.misses);
885    /// }
886    /// # Ok(())
887    /// # }
888    /// ```
889    #[cfg(feature = "descriptors")]
890    pub fn cache_stats(&self) -> Option<crate::descriptor::CacheStats> {
891        self.descriptor_cache.as_ref().map(|cache| cache.stats())
892    }
893
894    /// Clears all cached descriptors without disabling caching.
895    ///
896    /// Useful for forcing fresh descriptor downloads while keeping
897    /// caching enabled for future requests.
898    #[cfg(feature = "descriptors")]
899    pub fn clear_descriptor_cache(&mut self) {
900        if let Some(cache) = &self.descriptor_cache {
901            cache.clear();
902        }
903    }
904
905    /// Receives a response, buffering any asynchronous events.
906    ///
907    /// This internal method reads responses from the socket, automatically
908    /// buffering any asynchronous events (status code 650) that arrive
909    /// while waiting for a command response.
910    async fn recv_response(&mut self) -> Result<ControlMessage, Error> {
911        loop {
912            let response = self.socket.recv().await?;
913            if response.status_code == 650 {
914                self.event_buffer.push(response);
915            } else {
916                return Ok(response);
917            }
918        }
919    }
920
921    /// Authenticates with the Tor control interface.
922    ///
923    /// Attempts authentication using the best available method. If `password`
924    /// is provided, PASSWORD authentication is attempted. Otherwise, the method
925    /// is auto-detected from PROTOCOLINFO.
926    ///
927    /// # Authentication Methods
928    ///
929    /// Methods are tried in this order:
930    /// 1. **NONE** - If control port is open (no auth required)
931    /// 2. **SAFECOOKIE** - Preferred for local connections
932    /// 3. **COOKIE** - Fallback for older Tor versions
933    /// 4. **PASSWORD** - If password is provided
934    ///
935    /// # Arguments
936    ///
937    /// * `password` - Optional password for PASSWORD authentication
938    ///
939    /// # Preconditions
940    ///
941    /// - Socket must be connected (not closed)
942    /// - No prior successful authentication on this connection
943    ///
944    /// # Postconditions
945    ///
946    /// - On success: Controller is authenticated and ready for commands
947    /// - On failure: Connection state is undefined; reconnect recommended
948    ///
949    /// # Errors
950    ///
951    /// Returns [`Error::Authentication`] with specific reason:
952    ///
953    /// - [`AuthError::NoMethods`](crate::AuthError::NoMethods) - No compatible auth methods available
954    /// - [`AuthError::IncorrectPassword`](crate::AuthError::IncorrectPassword) - PASSWORD auth failed
955    /// - [`AuthError::CookieUnreadable`](crate::AuthError::CookieUnreadable) - Cannot read cookie file
956    /// - [`AuthError::IncorrectCookie`](crate::AuthError::IncorrectCookie) - COOKIE auth failed
957    /// - [`AuthError::ChallengeFailed`](crate::AuthError::ChallengeFailed) - SAFECOOKIE challenge failed
958    ///
959    /// # Example
960    ///
961    /// ```rust,no_run
962    /// use stem_rs::controller::Controller;
963    ///
964    /// # async fn example() -> Result<(), stem_rs::Error> {
965    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
966    ///
967    /// // Auto-detect authentication method
968    /// controller.authenticate(None).await?;
969    ///
970    /// // Or use password authentication
971    /// controller.authenticate(Some("my_password")).await?;
972    /// # Ok(())
973    /// # }
974    /// ```
975    ///
976    /// # Security
977    ///
978    /// - Passwords are cleared from memory after use
979    /// - Cookie comparison uses constant-time algorithm
980    /// - SAFECOOKIE nonces are cryptographically random
981    ///
982    /// # See Also
983    ///
984    /// - [`auth`]: Authentication implementation details
985    /// - [`AuthError`](crate::AuthError): Authentication error types
986    pub async fn authenticate(&mut self, password: Option<&str>) -> Result<(), Error> {
987        auth::authenticate(&mut self.socket, password).await
988    }
989
990    /// Queries Tor for information using the GETINFO command.
991    ///
992    /// GETINFO retrieves various pieces of information from Tor. The available
993    /// keys depend on Tor's version and configuration.
994    ///
995    /// # Arguments
996    ///
997    /// * `key` - The information key to query (e.g., "version", "circuit-status")
998    ///
999    /// # Common Keys
1000    ///
1001    /// | Key | Description |
1002    /// |-----|-------------|
1003    /// | `version` | Tor version string |
1004    /// | `process/pid` | Tor process ID |
1005    /// | `circuit-status` | Active circuit information |
1006    /// | `stream-status` | Active stream information |
1007    /// | `address` | Best guess at external IP address |
1008    /// | `fingerprint` | Relay fingerprint (if running as relay) |
1009    /// | `config-file` | Path to torrc file |
1010    ///
1011    /// # Errors
1012    ///
1013    /// Returns [`Error::OperationFailed`] if:
1014    /// - The key is unrecognized
1015    /// - The information is not available
1016    /// - Tor returns an error response
1017    ///
1018    /// # Example
1019    ///
1020    /// ```rust,no_run
1021    /// use stem_rs::controller::Controller;
1022    ///
1023    /// # async fn example() -> Result<(), stem_rs::Error> {
1024    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1025    /// controller.authenticate(None).await?;
1026    ///
1027    /// // Query Tor version
1028    /// let version = controller.get_info("version").await?;
1029    /// println!("Tor version: {}", version);
1030    ///
1031    /// // Query external IP address
1032    /// let address = controller.get_info("address").await?;
1033    /// println!("External IP: {}", address);
1034    /// # Ok(())
1035    /// # }
1036    /// ```
1037    ///
1038    /// # See Also
1039    ///
1040    /// - [`get_version`](Self::get_version): Typed version query
1041    /// - [`get_pid`](Self::get_pid): Typed PID query
1042    pub async fn get_info(&mut self, key: &str) -> Result<String, Error> {
1043        let command = format!("GETINFO {}", key);
1044        self.socket.send(&command).await?;
1045        let response = self.recv_response().await?;
1046
1047        if !response.is_ok() {
1048            return Err(Error::OperationFailed {
1049                code: response.status_code.to_string(),
1050                message: response.content().to_string(),
1051            });
1052        }
1053
1054        for line in &response.lines {
1055            if let Some(rest) = line.strip_prefix(&format!("{}=", key)) {
1056                return Ok(rest.to_string());
1057            }
1058            if line.starts_with(&format!("{}\n", key)) {
1059                return Ok(line
1060                    .strip_prefix(&format!("{}\n", key))
1061                    .unwrap_or("")
1062                    .to_string());
1063            }
1064        }
1065
1066        Ok(response.content().to_string())
1067    }
1068
1069    /// Retrieves the Tor version as a parsed [`Version`] object.
1070    ///
1071    /// This is a convenience wrapper around [`get_info("version")`](Self::get_info)
1072    /// that parses the version string into a structured [`Version`] type.
1073    ///
1074    /// # Errors
1075    ///
1076    /// Returns an error if:
1077    /// - The GETINFO command fails
1078    /// - The version string cannot be parsed
1079    ///
1080    /// # Example
1081    ///
1082    /// ```rust,no_run
1083    /// use stem_rs::controller::Controller;
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    /// let version = controller.get_version().await?;
1090    /// println!("Tor version: {}", version);
1091    ///
1092    /// // Version supports comparison
1093    /// // if version >= Version::parse("0.4.0.0")? { ... }
1094    /// # Ok(())
1095    /// # }
1096    /// ```
1097    ///
1098    /// # See Also
1099    ///
1100    /// - [`Version`]: Version type with comparison support
1101    pub async fn get_version(&mut self) -> Result<Version, Error> {
1102        let version_str = self.get_info("version").await?;
1103        Version::parse(&version_str)
1104    }
1105
1106    /// Retrieves the process ID of the Tor process.
1107    ///
1108    /// This is a convenience wrapper around [`get_info("process/pid")`](Self::get_info)
1109    /// that parses the PID into a `u32`.
1110    ///
1111    /// # Errors
1112    ///
1113    /// Returns an error if:
1114    /// - The GETINFO command fails
1115    /// - The PID string cannot be parsed as a number
1116    ///
1117    /// # Example
1118    ///
1119    /// ```rust,no_run
1120    /// use stem_rs::controller::Controller;
1121    ///
1122    /// # async fn example() -> Result<(), stem_rs::Error> {
1123    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1124    /// controller.authenticate(None).await?;
1125    ///
1126    /// let pid = controller.get_pid().await?;
1127    /// println!("Tor PID: {}", pid);
1128    /// # Ok(())
1129    /// # }
1130    /// ```
1131    pub async fn get_pid(&mut self) -> Result<u32, Error> {
1132        let pid_str = self.get_info("process/pid").await?;
1133        pid_str.parse().map_err(|_| Error::Parse {
1134            location: "pid".to_string(),
1135            reason: format!("invalid pid: {}", pid_str),
1136        })
1137    }
1138
1139    /// Retrieves the value(s) of a Tor configuration option.
1140    ///
1141    /// Uses the GETCONF command to query Tor's current configuration.
1142    /// Some options can have multiple values, so this returns a `Vec<String>`.
1143    ///
1144    /// # Arguments
1145    ///
1146    /// * `key` - The configuration option name (e.g., "SocksPort", "ExitPolicy")
1147    ///
1148    /// # Errors
1149    ///
1150    /// Returns [`Error::OperationFailed`] if:
1151    /// - The configuration option is unrecognized
1152    /// - Tor returns an error response
1153    ///
1154    /// # Example
1155    ///
1156    /// ```rust,no_run
1157    /// use stem_rs::controller::Controller;
1158    ///
1159    /// # async fn example() -> Result<(), stem_rs::Error> {
1160    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1161    /// controller.authenticate(None).await?;
1162    ///
1163    /// // Get SOCKS port configuration
1164    /// let socks_ports = controller.get_conf("SocksPort").await?;
1165    /// for port in socks_ports {
1166    ///     println!("SOCKS port: {}", port);
1167    /// }
1168    /// # Ok(())
1169    /// # }
1170    /// ```
1171    ///
1172    /// # See Also
1173    ///
1174    /// - [`set_conf`](Self::set_conf): Set a configuration option
1175    /// - [`reset_conf`](Self::reset_conf): Reset to default value
1176    pub async fn get_conf(&mut self, key: &str) -> Result<Vec<String>, Error> {
1177        let command = format!("GETCONF {}", key);
1178        self.socket.send(&command).await?;
1179        let response = self.recv_response().await?;
1180
1181        if !response.is_ok() {
1182            return Err(Error::OperationFailed {
1183                code: response.status_code.to_string(),
1184                message: response.content().to_string(),
1185            });
1186        }
1187
1188        let mut values = Vec::new();
1189        for line in &response.lines {
1190            if let Some(rest) = line.strip_prefix(&format!("{}=", key)) {
1191                values.push(rest.to_string());
1192            } else if line
1193                .to_lowercase()
1194                .starts_with(&format!("{}=", key.to_lowercase()))
1195            {
1196                let eq_pos = line.find('=').unwrap_or(line.len());
1197                values.push(line[eq_pos + 1..].to_string());
1198            }
1199        }
1200
1201        if values.is_empty() && !response.lines.is_empty() {
1202            let first_line = &response.lines[0];
1203            if let Some(eq_pos) = first_line.find('=') {
1204                values.push(first_line[eq_pos + 1..].to_string());
1205            }
1206        }
1207
1208        Ok(values)
1209    }
1210
1211    /// Sets a Tor configuration option.
1212    ///
1213    /// Uses the SETCONF command to change Tor's configuration at runtime.
1214    /// The change takes effect immediately but is not persisted to the torrc
1215    /// file unless you call `save_conf`.
1216    ///
1217    /// # Arguments
1218    ///
1219    /// * `key` - The configuration option name
1220    /// * `value` - The new value for the option
1221    ///
1222    /// # Value Escaping
1223    ///
1224    /// Values containing spaces or quotes are automatically escaped. You don't
1225    /// need to handle quoting yourself.
1226    ///
1227    /// # Errors
1228    ///
1229    /// Returns [`Error::OperationFailed`] if:
1230    /// - The configuration option is unrecognized
1231    /// - The value is invalid for this option
1232    /// - The option cannot be changed at runtime
1233    /// - Tor returns an error response
1234    ///
1235    /// # Example
1236    ///
1237    /// ```rust,no_run
1238    /// use stem_rs::controller::Controller;
1239    ///
1240    /// # async fn example() -> Result<(), stem_rs::Error> {
1241    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1242    /// controller.authenticate(None).await?;
1243    ///
1244    /// // Change bandwidth rate
1245    /// controller.set_conf("BandwidthRate", "1 MB").await?;
1246    ///
1247    /// // Enable a feature
1248    /// controller.set_conf("SafeLogging", "1").await?;
1249    /// # Ok(())
1250    /// # }
1251    /// ```
1252    ///
1253    /// # See Also
1254    ///
1255    /// - [`get_conf`](Self::get_conf): Get current configuration
1256    /// - [`reset_conf`](Self::reset_conf): Reset to default value
1257    pub async fn set_conf(&mut self, key: &str, value: &str) -> Result<(), Error> {
1258        let command = if value.contains(' ') || value.contains('"') {
1259            format!(
1260                "SETCONF {}=\"{}\"",
1261                key,
1262                value.replace('\\', "\\\\").replace('"', "\\\"")
1263            )
1264        } else {
1265            format!("SETCONF {}={}", key, value)
1266        };
1267        self.socket.send(&command).await?;
1268        let response = self.recv_response().await?;
1269
1270        if response.is_ok() {
1271            Ok(())
1272        } else {
1273            Err(Error::OperationFailed {
1274                code: response.status_code.to_string(),
1275                message: response.content().to_string(),
1276            })
1277        }
1278    }
1279
1280    /// Resets a Tor configuration option to its default value.
1281    ///
1282    /// Uses the RESETCONF command to restore a configuration option to its
1283    /// default value as if it were not set in the torrc file.
1284    ///
1285    /// # Arguments
1286    ///
1287    /// * `key` - The configuration option name to reset
1288    ///
1289    /// # Errors
1290    ///
1291    /// Returns [`Error::OperationFailed`] if:
1292    /// - The configuration option is unrecognized
1293    /// - The option cannot be reset at runtime
1294    /// - Tor returns an error response
1295    ///
1296    /// # Example
1297    ///
1298    /// ```rust,no_run
1299    /// use stem_rs::controller::Controller;
1300    ///
1301    /// # async fn example() -> Result<(), stem_rs::Error> {
1302    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1303    /// controller.authenticate(None).await?;
1304    ///
1305    /// // Reset bandwidth rate to default
1306    /// controller.reset_conf("BandwidthRate").await?;
1307    /// # Ok(())
1308    /// # }
1309    /// ```
1310    ///
1311    /// # See Also
1312    ///
1313    /// - [`get_conf`](Self::get_conf): Get current configuration
1314    /// - [`set_conf`](Self::set_conf): Set a configuration option
1315    pub async fn reset_conf(&mut self, key: &str) -> Result<(), Error> {
1316        let command = format!("RESETCONF {}", key);
1317        self.socket.send(&command).await?;
1318        let response = self.recv_response().await?;
1319
1320        if response.is_ok() {
1321            Ok(())
1322        } else {
1323            Err(Error::OperationFailed {
1324                code: response.status_code.to_string(),
1325                message: response.content().to_string(),
1326            })
1327        }
1328    }
1329
1330    /// Saves the current configuration to the torrc file.
1331    ///
1332    /// This persists any configuration changes made via [`set_conf`](Self::set_conf)
1333    /// to Tor's configuration file, so they survive restarts.
1334    ///
1335    /// # Arguments
1336    ///
1337    /// * `force` - If `true`, overwrite the configuration even if it includes
1338    ///   a `%include` clause. This is ignored if Tor doesn't support it.
1339    ///
1340    /// # Errors
1341    ///
1342    /// Returns [`Error::OperationFailed`] if:
1343    /// - Tor is unable to save the configuration file (e.g., permission denied)
1344    /// - The configuration file contains `%include` and `force` is `false`
1345    ///
1346    /// # Example
1347    ///
1348    /// ```rust,no_run
1349    /// use stem_rs::controller::Controller;
1350    ///
1351    /// # async fn example() -> Result<(), stem_rs::Error> {
1352    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1353    /// controller.authenticate(None).await?;
1354    ///
1355    /// // Change a configuration option
1356    /// controller.set_conf("BandwidthRate", "1 MB").await?;
1357    ///
1358    /// // Save to torrc
1359    /// controller.save_conf(false).await?;
1360    ///
1361    /// // Force save even with %include
1362    /// controller.save_conf(true).await?;
1363    /// # Ok(())
1364    /// # }
1365    /// ```
1366    ///
1367    /// # See Also
1368    ///
1369    /// - [`set_conf`](Self::set_conf): Set a configuration option
1370    /// - [`get_conf`](Self::get_conf): Get current configuration
1371    pub async fn save_conf(&mut self, force: bool) -> Result<(), Error> {
1372        let command = if force { "SAVECONF FORCE" } else { "SAVECONF" };
1373        self.socket.send(command).await?;
1374        let response = self.recv_response().await?;
1375
1376        if response.is_ok() {
1377            Ok(())
1378        } else if response.status_code == 551 {
1379            Err(Error::OperationFailed {
1380                code: response.status_code.to_string(),
1381                message: response.content().to_string(),
1382            })
1383        } else {
1384            Err(Error::Protocol(format!(
1385                "SAVECONF returned unexpected response code: {}",
1386                response.status_code
1387            )))
1388        }
1389    }
1390
1391    /// Sends a signal to the Tor process.
1392    ///
1393    /// Signals control various aspects of Tor's behavior, from requesting
1394    /// new circuits to initiating shutdown.
1395    ///
1396    /// # Arguments
1397    ///
1398    /// * `signal` - The signal to send (see [`Signal`])
1399    ///
1400    /// # Available Signals
1401    ///
1402    /// | Signal | Description |
1403    /// |--------|-------------|
1404    /// | [`Reload`](crate::Signal::Reload) | Reload configuration (SIGHUP) |
1405    /// | [`Shutdown`](crate::Signal::Shutdown) | Controlled shutdown |
1406    /// | [`Dump`](crate::Signal::Dump) | Write statistics to disk |
1407    /// | [`Debug`](crate::Signal::Debug) | Switch to debug logging |
1408    /// | [`Halt`](crate::Signal::Halt) | Immediate shutdown (SIGTERM) |
1409    /// | [`Newnym`](crate::Signal::Newnym) | Request new circuits |
1410    /// | [`ClearDnsCache`](crate::Signal::ClearDnsCache) | Clear DNS cache |
1411    /// | [`Heartbeat`](crate::Signal::Heartbeat) | Trigger heartbeat log |
1412    /// | [`Active`](crate::Signal::Active) | Wake from dormant mode |
1413    /// | [`Dormant`](crate::Signal::Dormant) | Enter dormant mode |
1414    ///
1415    /// # Errors
1416    ///
1417    /// Returns [`Error::OperationFailed`] if:
1418    /// - The signal is not recognized
1419    /// - The signal cannot be sent (e.g., rate-limited NEWNYM)
1420    /// - Tor returns an error response
1421    ///
1422    /// # Example
1423    ///
1424    /// ```rust,no_run
1425    /// use stem_rs::controller::Controller;
1426    /// use stem_rs::Signal;
1427    ///
1428    /// # async fn example() -> Result<(), stem_rs::Error> {
1429    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1430    /// controller.authenticate(None).await?;
1431    ///
1432    /// // Request new identity (new circuits)
1433    /// controller.signal(Signal::Newnym).await?;
1434    ///
1435    /// // Reload configuration
1436    /// controller.signal(Signal::Reload).await?;
1437    ///
1438    /// // Clear DNS cache
1439    /// controller.signal(Signal::ClearDnsCache).await?;
1440    /// # Ok(())
1441    /// # }
1442    /// ```
1443    ///
1444    /// # Rate Limiting
1445    ///
1446    /// The `Newnym` signal is rate-limited by Tor to prevent abuse. If called
1447    /// too frequently, Tor may delay the signal or return an error.
1448    ///
1449    /// # See Also
1450    ///
1451    /// - [`Signal`]: Signal enumeration
1452    pub async fn signal(&mut self, signal: Signal) -> Result<(), Error> {
1453        let command = format!("SIGNAL {}", signal);
1454        self.socket.send(&command).await?;
1455        let response = self.recv_response().await?;
1456
1457        if response.is_ok() {
1458            Ok(())
1459        } else {
1460            Err(Error::OperationFailed {
1461                code: response.status_code.to_string(),
1462                message: response.content().to_string(),
1463            })
1464        }
1465    }
1466
1467    /// Retrieves information about all active circuits.
1468    ///
1469    /// Returns a list of all circuits currently known to Tor, including
1470    /// their status and path information.
1471    ///
1472    /// # Errors
1473    ///
1474    /// Returns an error if:
1475    /// - The GETINFO command fails
1476    /// - The circuit status cannot be parsed
1477    ///
1478    /// # Example
1479    ///
1480    /// ```rust,no_run
1481    /// use stem_rs::controller::Controller;
1482    /// use stem_rs::CircStatus;
1483    ///
1484    /// # async fn example() -> Result<(), stem_rs::Error> {
1485    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1486    /// controller.authenticate(None).await?;
1487    ///
1488    /// let circuits = controller.get_circuits().await?;
1489    /// for circuit in circuits {
1490    ///     if circuit.status == CircStatus::Built {
1491    ///         println!("Circuit {} is ready with {} hops",
1492    ///             circuit.id, circuit.path.len());
1493    ///     }
1494    /// }
1495    /// # Ok(())
1496    /// # }
1497    /// ```
1498    ///
1499    /// # See Also
1500    ///
1501    /// - [`Circuit`]: Circuit information structure
1502    /// - [`new_circuit`](Self::new_circuit): Create a new circuit
1503    /// - [`close_circuit`](Self::close_circuit): Close a circuit
1504    pub async fn get_circuits(&mut self) -> Result<Vec<Circuit>, Error> {
1505        let response_str = self.get_info("circuit-status").await?;
1506        parse_circuits(&response_str)
1507    }
1508
1509    /// Creates a new circuit, optionally with a specified path.
1510    ///
1511    /// If no path is specified, Tor will select relays automatically based
1512    /// on its path selection algorithm. If a path is provided, Tor will
1513    /// attempt to build a circuit through those specific relays.
1514    ///
1515    /// # Arguments
1516    ///
1517    /// * `path` - Optional list of relay fingerprints or nicknames for the circuit path
1518    ///
1519    /// # Path Specification
1520    ///
1521    /// Relays can be specified by:
1522    /// - Fingerprint: `$9695DFC35FFEB861329B9F1AB04C46397020CE31`
1523    /// - Nickname: `MyRelay`
1524    /// - Fingerprint with nickname: `$9695DFC35FFEB861329B9F1AB04C46397020CE31~MyRelay`
1525    ///
1526    /// # Errors
1527    ///
1528    /// Returns [`Error::OperationFailed`] if:
1529    /// - A specified relay is unknown or unavailable
1530    /// - The path is invalid (e.g., too short)
1531    /// - Circuit creation fails
1532    ///
1533    /// # Example
1534    ///
1535    /// ```rust,no_run
1536    /// use stem_rs::controller::Controller;
1537    ///
1538    /// # async fn example() -> Result<(), stem_rs::Error> {
1539    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1540    /// controller.authenticate(None).await?;
1541    ///
1542    /// // Create circuit with automatic path selection
1543    /// let circuit_id = controller.new_circuit(None).await?;
1544    /// println!("Created circuit: {}", circuit_id);
1545    ///
1546    /// // Create circuit with specific path
1547    /// let path = &["$AAAA...", "$BBBB...", "$CCCC..."];
1548    /// let circuit_id = controller.new_circuit(Some(path)).await?;
1549    /// # Ok(())
1550    /// # }
1551    /// ```
1552    ///
1553    /// # See Also
1554    ///
1555    /// - [`extend_circuit`](Self::extend_circuit): Extend an existing circuit
1556    /// - [`close_circuit`](Self::close_circuit): Close a circuit
1557    /// - [`get_circuits`](Self::get_circuits): List active circuits
1558    pub async fn new_circuit(&mut self, path: Option<&[&str]>) -> Result<CircuitId, Error> {
1559        let command = match path {
1560            Some(relays) if !relays.is_empty() => {
1561                format!("EXTENDCIRCUIT 0 {}", relays.join(","))
1562            }
1563            _ => "EXTENDCIRCUIT 0".to_string(),
1564        };
1565        self.socket.send(&command).await?;
1566        let response = self.recv_response().await?;
1567
1568        if !response.is_ok() {
1569            return Err(Error::OperationFailed {
1570                code: response.status_code.to_string(),
1571                message: response.content().to_string(),
1572            });
1573        }
1574
1575        let content = response.content();
1576        let mut line = ControlLine::new(content);
1577        if line.is_next_mapping(Some("EXTENDED"), false) {
1578            let (_, circuit_id) = line.pop_mapping(false, false)?;
1579            return Ok(CircuitId::new(circuit_id));
1580        }
1581
1582        let circuit_id = line.pop(false, false)?;
1583        Ok(CircuitId::new(circuit_id))
1584    }
1585
1586    /// Extends an existing circuit by adding additional hops.
1587    ///
1588    /// Adds one or more relays to an existing circuit. The circuit must be
1589    /// in a state that allows extension (typically BUILT or EXTENDED).
1590    ///
1591    /// # Arguments
1592    ///
1593    /// * `id` - The circuit ID to extend
1594    /// * `path` - List of relay fingerprints or nicknames to add
1595    ///
1596    /// # Errors
1597    ///
1598    /// Returns [`Error::InvalidArguments`] if:
1599    /// - The path is empty
1600    ///
1601    /// Returns [`Error::CircuitExtensionFailed`] if:
1602    /// - The circuit doesn't exist
1603    /// - The circuit is in a state that doesn't allow extension
1604    /// - A specified relay is unknown or unavailable
1605    /// - The extension fails for any other reason
1606    ///
1607    /// # Example
1608    ///
1609    /// ```rust,no_run
1610    /// use stem_rs::controller::Controller;
1611    ///
1612    /// # async fn example() -> Result<(), stem_rs::Error> {
1613    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1614    /// controller.authenticate(None).await?;
1615    ///
1616    /// // Create a circuit and extend it
1617    /// let circuit_id = controller.new_circuit(None).await?;
1618    /// controller.extend_circuit(&circuit_id, &["$DDDD..."]).await?;
1619    /// # Ok(())
1620    /// # }
1621    /// ```
1622    ///
1623    /// # See Also
1624    ///
1625    /// - [`new_circuit`](Self::new_circuit): Create a new circuit
1626    /// - [`close_circuit`](Self::close_circuit): Close a circuit
1627    pub async fn extend_circuit(&mut self, id: &CircuitId, path: &[&str]) -> Result<(), Error> {
1628        if path.is_empty() {
1629            return Err(Error::InvalidArguments("path cannot be empty".to_string()));
1630        }
1631        let command = format!("EXTENDCIRCUIT {} {}", id.0, path.join(","));
1632        self.socket.send(&command).await?;
1633        let response = self.recv_response().await?;
1634
1635        if response.is_ok() {
1636            Ok(())
1637        } else {
1638            Err(Error::CircuitExtensionFailed(
1639                response.content().to_string(),
1640            ))
1641        }
1642    }
1643
1644    /// Closes an existing circuit.
1645    ///
1646    /// Tears down the specified circuit, closing all streams attached to it.
1647    ///
1648    /// # Arguments
1649    ///
1650    /// * `id` - The circuit ID to close
1651    ///
1652    /// # Errors
1653    ///
1654    /// Returns [`Error::OperationFailed`] if:
1655    /// - The circuit doesn't exist
1656    /// - The circuit is already closed
1657    /// - Tor returns an error response
1658    ///
1659    /// # Example
1660    ///
1661    /// ```rust,no_run
1662    /// use stem_rs::controller::Controller;
1663    ///
1664    /// # async fn example() -> Result<(), stem_rs::Error> {
1665    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1666    /// controller.authenticate(None).await?;
1667    ///
1668    /// // Create and then close a circuit
1669    /// let circuit_id = controller.new_circuit(None).await?;
1670    /// controller.close_circuit(&circuit_id).await?;
1671    /// # Ok(())
1672    /// # }
1673    /// ```
1674    ///
1675    /// # See Also
1676    ///
1677    /// - [`new_circuit`](Self::new_circuit): Create a new circuit
1678    /// - [`get_circuits`](Self::get_circuits): List active circuits
1679    pub async fn close_circuit(&mut self, id: &CircuitId) -> Result<(), Error> {
1680        let command = format!("CLOSECIRCUIT {}", id.0);
1681        self.socket.send(&command).await?;
1682        let response = self.recv_response().await?;
1683
1684        if response.is_ok() {
1685            Ok(())
1686        } else {
1687            Err(Error::OperationFailed {
1688                code: response.status_code.to_string(),
1689                message: response.content().to_string(),
1690            })
1691        }
1692    }
1693
1694    /// Retrieves information about all active streams.
1695    ///
1696    /// Returns a list of all streams currently known to Tor, including
1697    /// their status, target, and circuit attachment.
1698    ///
1699    /// # Errors
1700    ///
1701    /// Returns an error if:
1702    /// - The GETINFO command fails
1703    /// - The stream status cannot be parsed
1704    ///
1705    /// # Example
1706    ///
1707    /// ```rust,no_run
1708    /// use stem_rs::controller::Controller;
1709    /// use stem_rs::StreamStatus;
1710    ///
1711    /// # async fn example() -> Result<(), stem_rs::Error> {
1712    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1713    /// controller.authenticate(None).await?;
1714    ///
1715    /// let streams = controller.get_streams().await?;
1716    /// for stream in streams {
1717    ///     println!("Stream {} -> {}:{} ({:?})",
1718    ///         stream.id, stream.target_host, stream.target_port, stream.status);
1719    /// }
1720    /// # Ok(())
1721    /// # }
1722    /// ```
1723    ///
1724    /// # See Also
1725    ///
1726    /// - [`Stream`]: Stream information structure
1727    /// - [`attach_stream`](Self::attach_stream): Attach a stream to a circuit
1728    /// - [`close_stream`](Self::close_stream): Close a stream
1729    pub async fn get_streams(&mut self) -> Result<Vec<Stream>, Error> {
1730        let response_str = self.get_info("stream-status").await?;
1731        parse_streams(&response_str)
1732    }
1733
1734    /// Attaches a stream to a specific circuit.
1735    ///
1736    /// Manually attaches a stream to a circuit. This is typically used when
1737    /// you want to control which circuit a stream uses, rather than letting
1738    /// Tor choose automatically.
1739    ///
1740    /// # Arguments
1741    ///
1742    /// * `stream_id` - The stream to attach
1743    /// * `circuit_id` - The circuit to attach the stream to
1744    ///
1745    /// # Preconditions
1746    ///
1747    /// - The stream must be in a state that allows attachment (typically NEW)
1748    /// - The circuit must be BUILT
1749    /// - The circuit's exit policy must allow the stream's target
1750    ///
1751    /// # Errors
1752    ///
1753    /// Returns [`Error::OperationFailed`] if:
1754    /// - The stream doesn't exist
1755    /// - The circuit doesn't exist
1756    /// - The stream is not in an attachable state
1757    /// - The circuit cannot handle the stream's target
1758    ///
1759    /// # Example
1760    ///
1761    /// ```rust,no_run
1762    /// use stem_rs::controller::{Controller, CircuitId, StreamId};
1763    ///
1764    /// # async fn example() -> Result<(), stem_rs::Error> {
1765    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1766    /// controller.authenticate(None).await?;
1767    ///
1768    /// // Attach stream 1 to circuit 5
1769    /// let stream_id = StreamId::new("1");
1770    /// let circuit_id = CircuitId::new("5");
1771    /// controller.attach_stream(&stream_id, &circuit_id).await?;
1772    /// # Ok(())
1773    /// # }
1774    /// ```
1775    ///
1776    /// # See Also
1777    ///
1778    /// - [`get_streams`](Self::get_streams): List active streams
1779    /// - [`close_stream`](Self::close_stream): Close a stream
1780    pub async fn attach_stream(
1781        &mut self,
1782        stream_id: &StreamId,
1783        circuit_id: &CircuitId,
1784    ) -> Result<(), Error> {
1785        let command = format!("ATTACHSTREAM {} {}", stream_id.0, circuit_id.0);
1786        self.socket.send(&command).await?;
1787        let response = self.recv_response().await?;
1788
1789        if response.is_ok() {
1790            Ok(())
1791        } else {
1792            Err(Error::OperationFailed {
1793                code: response.status_code.to_string(),
1794                message: response.content().to_string(),
1795            })
1796        }
1797    }
1798
1799    /// Closes an existing stream.
1800    ///
1801    /// Terminates the specified stream with an optional reason code.
1802    ///
1803    /// # Arguments
1804    ///
1805    /// * `id` - The stream ID to close
1806    /// * `reason` - Optional reason code (defaults to 1 = MISC if not specified)
1807    ///
1808    /// # Reason Codes
1809    ///
1810    /// Common reason codes include:
1811    /// - 1: MISC (miscellaneous)
1812    /// - 2: RESOLVEFAILED (DNS resolution failed)
1813    /// - 3: CONNECTREFUSED (connection refused)
1814    /// - 4: EXITPOLICY (exit policy violation)
1815    /// - 5: DESTROY (circuit destroyed)
1816    /// - 6: DONE (stream finished normally)
1817    /// - 7: TIMEOUT (connection timeout)
1818    ///
1819    /// # Errors
1820    ///
1821    /// Returns [`Error::OperationFailed`] if:
1822    /// - The stream doesn't exist
1823    /// - The stream is already closed
1824    /// - Tor returns an error response
1825    ///
1826    /// # Example
1827    ///
1828    /// ```rust,no_run
1829    /// use stem_rs::controller::{Controller, StreamId};
1830    ///
1831    /// # async fn example() -> Result<(), stem_rs::Error> {
1832    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1833    /// controller.authenticate(None).await?;
1834    ///
1835    /// // Close stream with default reason
1836    /// let stream_id = StreamId::new("1");
1837    /// controller.close_stream(&stream_id, None).await?;
1838    ///
1839    /// // Close stream with specific reason (DONE)
1840    /// controller.close_stream(&stream_id, Some(6)).await?;
1841    /// # Ok(())
1842    /// # }
1843    /// ```
1844    ///
1845    /// # See Also
1846    ///
1847    /// - [`get_streams`](Self::get_streams): List active streams
1848    /// - [`attach_stream`](Self::attach_stream): Attach a stream to a circuit
1849    pub async fn close_stream(&mut self, id: &StreamId, reason: Option<u8>) -> Result<(), Error> {
1850        let command = match reason {
1851            Some(r) => format!("CLOSESTREAM {} {}", id.0, r),
1852            None => format!("CLOSESTREAM {} 1", id.0),
1853        };
1854        self.socket.send(&command).await?;
1855        let response = self.recv_response().await?;
1856
1857        if response.is_ok() {
1858            Ok(())
1859        } else {
1860            Err(Error::OperationFailed {
1861                code: response.status_code.to_string(),
1862                message: response.content().to_string(),
1863            })
1864        }
1865    }
1866
1867    /// Maps one address to another for Tor connections.
1868    ///
1869    /// Creates an address mapping so that connections to the `from` address
1870    /// are redirected to the `to` address. This is useful for creating
1871    /// virtual addresses or redirecting traffic.
1872    ///
1873    /// # Arguments
1874    ///
1875    /// * `from` - The source address to map from
1876    /// * `to` - The destination address to map to
1877    ///
1878    /// # Returns
1879    ///
1880    /// Returns a `HashMap` containing the established mappings. The keys are
1881    /// the source addresses and values are the destination addresses.
1882    ///
1883    /// # Errors
1884    ///
1885    /// Returns [`Error::OperationFailed`] if:
1886    /// - The address format is invalid
1887    /// - The mapping cannot be created
1888    /// - Tor returns an error response
1889    ///
1890    /// # Example
1891    ///
1892    /// ```rust,no_run
1893    /// use stem_rs::controller::Controller;
1894    ///
1895    /// # async fn example() -> Result<(), stem_rs::Error> {
1896    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1897    /// controller.authenticate(None).await?;
1898    ///
1899    /// // Map a hostname to a .onion address
1900    /// let mappings = controller.map_address(
1901    ///     "www.example.com",
1902    ///     "exampleonion.onion"
1903    /// ).await?;
1904    ///
1905    /// for (from, to) in mappings {
1906    ///     println!("{} -> {}", from, to);
1907    /// }
1908    /// # Ok(())
1909    /// # }
1910    /// ```
1911    pub async fn map_address(
1912        &mut self,
1913        from: &str,
1914        to: &str,
1915    ) -> Result<HashMap<String, String>, Error> {
1916        let command = format!("MAPADDRESS {}={}", from, to);
1917        self.socket.send(&command).await?;
1918        let response = self.recv_response().await?;
1919
1920        if !response.is_ok() {
1921            return Err(Error::OperationFailed {
1922                code: response.status_code.to_string(),
1923                message: response.content().to_string(),
1924            });
1925        }
1926
1927        let mut mappings = HashMap::new();
1928        for line in &response.lines {
1929            if let Some(eq_pos) = line.find('=') {
1930                let key = line[..eq_pos].to_string();
1931                let value = line[eq_pos + 1..].to_string();
1932                mappings.insert(key, value);
1933            }
1934        }
1935        Ok(mappings)
1936    }
1937
1938    /// Subscribes to asynchronous events from Tor.
1939    ///
1940    /// Configures which event types Tor should send to this controller.
1941    /// Events are received via [`recv_event`](Self::recv_event).
1942    ///
1943    /// # Arguments
1944    ///
1945    /// * `events` - List of event types to subscribe to
1946    ///
1947    /// # Event Types
1948    ///
1949    /// Common event types include:
1950    /// - [`EventType::Circ`] - Circuit status changes
1951    /// - [`EventType::Stream`] - Stream status changes
1952    /// - [`EventType::Bw`] - Bandwidth usage
1953    /// - [`EventType::Notice`] - Notice-level log messages
1954    /// - [`EventType::Warn`] - Warning-level log messages
1955    ///
1956    /// # Errors
1957    ///
1958    /// Returns [`Error::OperationFailed`] if:
1959    /// - An event type is not recognized
1960    /// - Tor returns an error response
1961    ///
1962    /// # Example
1963    ///
1964    /// ```rust,no_run
1965    /// use stem_rs::controller::Controller;
1966    /// use stem_rs::EventType;
1967    ///
1968    /// # async fn example() -> Result<(), stem_rs::Error> {
1969    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1970    /// controller.authenticate(None).await?;
1971    ///
1972    /// // Subscribe to circuit and bandwidth events
1973    /// controller.set_events(&[EventType::Circ, EventType::Bw]).await?;
1974    ///
1975    /// // Receive events
1976    /// loop {
1977    ///     let event = controller.recv_event().await?;
1978    ///     println!("Received event: {:?}", event);
1979    /// }
1980    /// # Ok(())
1981    /// # }
1982    /// ```
1983    ///
1984    /// # Clearing Subscriptions
1985    ///
1986    /// To stop receiving events, call with an empty slice:
1987    ///
1988    /// ```rust,no_run
1989    /// # use stem_rs::controller::Controller;
1990    /// # async fn example() -> Result<(), stem_rs::Error> {
1991    /// # let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1992    /// controller.set_events(&[]).await?; // Clear all subscriptions
1993    /// # Ok(())
1994    /// # }
1995    /// ```
1996    ///
1997    /// # See Also
1998    ///
1999    /// - [`recv_event`](Self::recv_event): Receive subscribed events
2000    /// - [`EventType`]: Available event types
2001    /// - [`events`](crate::events): Event parsing module
2002    pub async fn set_events(&mut self, events: &[EventType]) -> Result<(), Error> {
2003        let event_names: Vec<String> = events.iter().map(|e| e.to_string()).collect();
2004        let command = if event_names.is_empty() {
2005            "SETEVENTS".to_string()
2006        } else {
2007            format!("SETEVENTS {}", event_names.join(" "))
2008        };
2009        self.socket.send(&command).await?;
2010        let response = self.recv_response().await?;
2011
2012        if response.is_ok() {
2013            Ok(())
2014        } else {
2015            Err(Error::OperationFailed {
2016                code: response.status_code.to_string(),
2017                message: response.content().to_string(),
2018            })
2019        }
2020    }
2021
2022    /// Receives the next asynchronous event from Tor.
2023    ///
2024    /// Blocks until an event is available. Events must first be subscribed
2025    /// to using [`set_events`](Self::set_events).
2026    ///
2027    /// # Event Buffering
2028    ///
2029    /// Events that arrive while waiting for command responses are automatically
2030    /// buffered and returned by subsequent calls to this method.
2031    ///
2032    /// # Errors
2033    ///
2034    /// Returns [`Error::Protocol`] if:
2035    /// - The received message is not an event (status code != 650)
2036    ///
2037    /// Returns [`Error::Socket`] if:
2038    /// - The connection is closed
2039    /// - A network error occurs
2040    ///
2041    /// # Example
2042    ///
2043    /// ```rust,no_run
2044    /// use stem_rs::controller::Controller;
2045    /// use stem_rs::EventType;
2046    /// use stem_rs::events::ParsedEvent;
2047    ///
2048    /// # async fn example() -> Result<(), stem_rs::Error> {
2049    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2050    /// controller.authenticate(None).await?;
2051    ///
2052    /// // Subscribe to bandwidth events
2053    /// controller.set_events(&[EventType::Bw]).await?;
2054    ///
2055    /// // Receive and process events
2056    /// loop {
2057    ///     match controller.recv_event().await? {
2058    ///         ParsedEvent::Bandwidth(bw) => {
2059    ///             println!("Bandwidth: {} read, {} written", bw.read, bw.written);
2060    ///         }
2061    ///         other => println!("Other event: {:?}", other),
2062    ///     }
2063    /// }
2064    /// # Ok(())
2065    /// # }
2066    /// ```
2067    ///
2068    /// # See Also
2069    ///
2070    /// - [`set_events`](Self::set_events): Subscribe to events
2071    /// - [`ParsedEvent`]: Event types
2072    pub async fn recv_event(&mut self) -> Result<ParsedEvent, Error> {
2073        let response = if let Some(buffered) = self.event_buffer.pop() {
2074            buffered
2075        } else {
2076            self.socket.recv().await?
2077        };
2078
2079        if response.status_code != 650 {
2080            return Err(Error::Protocol(format!(
2081                "expected async event (650), got {}",
2082                response.status_code
2083            )));
2084        }
2085
2086        let content = response.content();
2087        let (event_type, event_content) = content.split_once(' ').unwrap_or((content, ""));
2088
2089        let lines: Vec<String> = response
2090            .lines
2091            .iter()
2092            .skip(1)
2093            .filter(|l| !l.is_empty() && *l != "OK")
2094            .cloned()
2095            .collect();
2096
2097        ParsedEvent::parse(event_type, event_content, Some(&lines))
2098    }
2099
2100    /// Sends a raw command to Tor and returns the response.
2101    ///
2102    /// This is a low-level method for sending arbitrary control protocol
2103    /// commands. For most use cases, prefer the typed methods like
2104    /// [`get_info`](Self::get_info), [`signal`](Self::signal), etc.
2105    ///
2106    /// # Arguments
2107    ///
2108    /// * `command` - The raw command string to send
2109    ///
2110    /// # Errors
2111    ///
2112    /// Returns [`Error::OperationFailed`] if:
2113    /// - Tor returns an error response
2114    ///
2115    /// Returns [`Error::Socket`] if:
2116    /// - The connection is closed
2117    /// - A network error occurs
2118    ///
2119    /// # Example
2120    ///
2121    /// ```rust,no_run
2122    /// use stem_rs::controller::Controller;
2123    ///
2124    /// # async fn example() -> Result<(), stem_rs::Error> {
2125    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2126    /// controller.authenticate(None).await?;
2127    ///
2128    /// // Send a raw GETINFO command
2129    /// let response = controller.msg("GETINFO version").await?;
2130    /// println!("Raw response: {}", response);
2131    /// # Ok(())
2132    /// # }
2133    /// ```
2134    ///
2135    /// # See Also
2136    ///
2137    /// - [`get_info`](Self::get_info): Typed GETINFO wrapper
2138    /// - [`signal`](Self::signal): Typed SIGNAL wrapper
2139    pub async fn msg(&mut self, command: &str) -> Result<String, Error> {
2140        self.socket.send(command).await?;
2141        let response = self.recv_response().await?;
2142
2143        if !response.is_ok() {
2144            return Err(Error::OperationFailed {
2145                code: response.status_code.to_string(),
2146                message: response.content().to_string(),
2147            });
2148        }
2149
2150        Ok(response.raw_content())
2151    }
2152
2153    /// Creates an ephemeral hidden service.
2154    ///
2155    /// Unlike file-based hidden services, ephemeral services don't touch disk
2156    /// and are the recommended way to create hidden services programmatically.
2157    ///
2158    /// # Arguments
2159    ///
2160    /// * `ports` - Mapping of virtual ports to local targets (e.g., `[(80, "127.0.0.1:8080")]`)
2161    /// * `key_type` - Type of key: `"NEW"` to generate, `"RSA1024"`, or `"ED25519-V3"`
2162    /// * `key_content` - Key content or type to generate (`"BEST"`, `"RSA1024"`, `"ED25519-V3"`)
2163    /// * `flags` - Optional flags like `"Detach"`, `"DiscardPK"`, `"BasicAuth"`, `"MaxStreamsCloseCircuit"`
2164    ///
2165    /// # Returns
2166    ///
2167    /// Returns an [`AddOnionResponse`] containing:
2168    /// - `service_id`: The onion address (without `.onion` suffix)
2169    /// - `private_key`: The private key (unless `DiscardPK` flag was set)
2170    /// - `private_key_type`: The key type (e.g., `"ED25519-V3"`)
2171    ///
2172    /// # Example
2173    ///
2174    /// ```rust,no_run
2175    /// use stem_rs::controller::Controller;
2176    ///
2177    /// # async fn example() -> Result<(), stem_rs::Error> {
2178    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2179    /// controller.authenticate(None).await?;
2180    ///
2181    /// // Create a v3 hidden service mapping port 80 to local port 8080
2182    /// let response = controller.create_ephemeral_hidden_service(
2183    ///     &[(80, "127.0.0.1:8080")],
2184    ///     "NEW",
2185    ///     "ED25519-V3",
2186    ///     &[],
2187    /// ).await?;
2188    ///
2189    /// println!("Hidden service: {}.onion", response.service_id);
2190    /// # Ok(())
2191    /// # }
2192    /// ```
2193    ///
2194    /// # See Also
2195    ///
2196    /// - [`remove_ephemeral_hidden_service`](Self::remove_ephemeral_hidden_service): Remove the service
2197    pub async fn create_ephemeral_hidden_service(
2198        &mut self,
2199        ports: &[(u16, &str)],
2200        key_type: &str,
2201        key_content: &str,
2202        flags: &[&str],
2203    ) -> Result<AddOnionResponse, Error> {
2204        let mut request = format!("ADD_ONION {}:{}", key_type, key_content);
2205
2206        if !flags.is_empty() {
2207            request.push_str(&format!(" Flags={}", flags.join(",")));
2208        }
2209
2210        for (virt_port, target) in ports {
2211            request.push_str(&format!(" Port={},{}", virt_port, target));
2212        }
2213
2214        self.socket.send(&request).await?;
2215        let response = self.recv_response().await?;
2216
2217        if !response.is_ok() {
2218            return Err(Error::OperationFailed {
2219                code: response.status_code.to_string(),
2220                message: response.content().to_string(),
2221            });
2222        }
2223
2224        parse_add_onion_response(&response.all_content())
2225    }
2226
2227    /// Removes an ephemeral hidden service.
2228    ///
2229    /// Discontinues a hidden service that was created with
2230    /// [`create_ephemeral_hidden_service`](Self::create_ephemeral_hidden_service).
2231    ///
2232    /// # Arguments
2233    ///
2234    /// * `service_id` - The onion address without the `.onion` suffix
2235    ///
2236    /// # Returns
2237    ///
2238    /// Returns `true` if the service was removed, `false` if it wasn't running.
2239    ///
2240    /// # Example
2241    ///
2242    /// ```rust,no_run
2243    /// use stem_rs::controller::Controller;
2244    ///
2245    /// # async fn example() -> Result<(), stem_rs::Error> {
2246    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2247    /// controller.authenticate(None).await?;
2248    ///
2249    /// // Create and then remove a hidden service
2250    /// let response = controller.create_ephemeral_hidden_service(
2251    ///     &[(80, "127.0.0.1:8080")],
2252    ///     "NEW",
2253    ///     "BEST",
2254    ///     &[],
2255    /// ).await?;
2256    ///
2257    /// controller.remove_ephemeral_hidden_service(&response.service_id).await?;
2258    /// # Ok(())
2259    /// # }
2260    /// ```
2261    pub async fn remove_ephemeral_hidden_service(
2262        &mut self,
2263        service_id: &str,
2264    ) -> Result<bool, Error> {
2265        let command = format!("DEL_ONION {}", service_id);
2266        match self.msg(&command).await {
2267            Ok(_) => Ok(true),
2268            Err(Error::OperationFailed { code, message }) => {
2269                if message.contains("Unknown Onion Service") {
2270                    Ok(false)
2271                } else {
2272                    Err(Error::OperationFailed { code, message })
2273                }
2274            }
2275            Err(e) => Err(e),
2276        }
2277    }
2278
2279    /// Loads configuration text as if it were read from the torrc.
2280    ///
2281    /// This allows dynamically configuring Tor without modifying the torrc file.
2282    /// The configuration text is processed as if it were part of the torrc.
2283    ///
2284    /// # Arguments
2285    ///
2286    /// * `config_text` - The configuration text to load
2287    ///
2288    /// # Errors
2289    ///
2290    /// Returns [`Error::InvalidRequest`] if:
2291    /// - The configuration text contains invalid options
2292    /// - The configuration text has syntax errors
2293    ///
2294    /// Returns [`Error::InvalidArguments`] if:
2295    /// - An unknown configuration option is specified
2296    ///
2297    /// # Example
2298    ///
2299    /// ```rust,no_run
2300    /// use stem_rs::controller::Controller;
2301    ///
2302    /// # async fn example() -> Result<(), stem_rs::Error> {
2303    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2304    /// controller.authenticate(None).await?;
2305    ///
2306    /// // Load configuration
2307    /// controller.load_conf("MaxCircuitDirtiness 600").await?;
2308    /// # Ok(())
2309    /// # }
2310    /// ```
2311    ///
2312    /// # See Also
2313    ///
2314    /// - [`set_conf`](Self::set_conf): Set individual configuration options
2315    /// - [`save_conf`](Self::save_conf): Save configuration to torrc
2316    pub async fn load_conf(&mut self, config_text: &str) -> Result<(), Error> {
2317        let command = format!("LOADCONF\n{}", config_text);
2318        self.socket.send(&command).await?;
2319        let response = self.recv_response().await?;
2320
2321        if response.is_ok() {
2322            Ok(())
2323        } else if response.status_code == 552 || response.status_code == 553 {
2324            let message = response.content().to_string();
2325            if response.status_code == 552 && message.contains("Unknown option") {
2326                // Extract the unknown option name
2327                Err(Error::InvalidArguments(message))
2328            } else {
2329                Err(Error::InvalidRequest(message))
2330            }
2331        } else {
2332            Err(Error::Protocol(format!(
2333                "LOADCONF received unexpected response: {}",
2334                response.status_code
2335            )))
2336        }
2337    }
2338
2339    /// Drops guard nodes and optionally resets circuit timeouts.
2340    ///
2341    /// This forces Tor to drop its current guard nodes and select new ones.
2342    /// Optionally, circuit build timeout counters can also be reset.
2343    ///
2344    /// # Arguments
2345    ///
2346    /// * `reset_timeouts` - If `true`, also reset circuit build timeout counters
2347    ///
2348    /// # Errors
2349    ///
2350    /// Returns [`Error::OperationFailed`] if:
2351    /// - Tor returns an error response
2352    /// - `reset_timeouts` is `true` but Tor version doesn't support DROPTIMEOUTS
2353    ///
2354    /// # Example
2355    ///
2356    /// ```rust,no_run
2357    /// use stem_rs::controller::Controller;
2358    ///
2359    /// # async fn example() -> Result<(), stem_rs::Error> {
2360    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2361    /// controller.authenticate(None).await?;
2362    ///
2363    /// // Drop guards only
2364    /// controller.drop_guards(false).await?;
2365    ///
2366    /// // Drop guards and reset timeouts
2367    /// controller.drop_guards(true).await?;
2368    /// # Ok(())
2369    /// # }
2370    /// ```
2371    pub async fn drop_guards(&mut self, reset_timeouts: bool) -> Result<(), Error> {
2372        self.socket.send("DROPGUARDS").await?;
2373        let response = self.recv_response().await?;
2374
2375        if !response.is_ok() {
2376            return Err(Error::OperationFailed {
2377                code: response.status_code.to_string(),
2378                message: response.content().to_string(),
2379            });
2380        }
2381
2382        if reset_timeouts {
2383            self.socket.send("DROPTIMEOUTS").await?;
2384            let response = self.recv_response().await?;
2385
2386            if !response.is_ok() {
2387                return Err(Error::OperationFailed {
2388                    code: response.status_code.to_string(),
2389                    message: response.content().to_string(),
2390                });
2391            }
2392        }
2393
2394        Ok(())
2395    }
2396
2397    /// Changes a circuit's purpose.
2398    ///
2399    /// Currently, two purposes are recognized: "general" and "controller".
2400    ///
2401    /// # Arguments
2402    ///
2403    /// * `circuit_id` - The ID of the circuit to repurpose
2404    /// * `purpose` - The new purpose for the circuit
2405    ///
2406    /// # Errors
2407    ///
2408    /// Returns [`Error::InvalidRequest`] if:
2409    /// - The circuit doesn't exist
2410    /// - The purpose is invalid
2411    ///
2412    /// # Example
2413    ///
2414    /// ```rust,no_run
2415    /// use stem_rs::controller::{Controller, CircuitId, CircuitPurpose};
2416    ///
2417    /// # async fn example() -> Result<(), stem_rs::Error> {
2418    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2419    /// controller.authenticate(None).await?;
2420    ///
2421    /// let circuit_id = CircuitId::new("5");
2422    /// controller.repurpose_circuit(&circuit_id, CircuitPurpose::Controller).await?;
2423    /// # Ok(())
2424    /// # }
2425    /// ```
2426    pub async fn repurpose_circuit(
2427        &mut self,
2428        circuit_id: &CircuitId,
2429        purpose: CircuitPurpose,
2430    ) -> Result<(), Error> {
2431        let command = format!("SETCIRCUITPURPOSE {} purpose={}", circuit_id.0, purpose);
2432        self.socket.send(&command).await?;
2433        let response = self.recv_response().await?;
2434
2435        if response.is_ok() {
2436            Ok(())
2437        } else if response.status_code == 552 {
2438            Err(Error::InvalidRequest(response.content().to_string()))
2439        } else {
2440            Err(Error::Protocol(format!(
2441                "SETCIRCUITPURPOSE returned unexpected response code: {}",
2442                response.status_code
2443            )))
2444        }
2445    }
2446
2447    /// Enables controller features that are disabled by default.
2448    ///
2449    /// Once enabled, a feature cannot be disabled and a new control connection
2450    /// must be opened to get a connection with the feature disabled.
2451    ///
2452    /// # Arguments
2453    ///
2454    /// * `features` - List of feature names to enable
2455    ///
2456    /// # Errors
2457    ///
2458    /// Returns [`Error::InvalidArguments`] if:
2459    /// - An unrecognized feature is specified
2460    ///
2461    /// # Example
2462    ///
2463    /// ```rust,no_run
2464    /// use stem_rs::controller::Controller;
2465    ///
2466    /// # async fn example() -> Result<(), stem_rs::Error> {
2467    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2468    /// controller.authenticate(None).await?;
2469    ///
2470    /// controller.enable_feature(&["VERBOSE_NAMES"]).await?;
2471    /// # Ok(())
2472    /// # }
2473    /// ```
2474    pub async fn enable_feature(&mut self, features: &[&str]) -> Result<(), Error> {
2475        let command = format!("USEFEATURE {}", features.join(" "));
2476        self.socket.send(&command).await?;
2477        let response = self.recv_response().await?;
2478
2479        if response.is_ok() {
2480            Ok(())
2481        } else if response.status_code == 552 {
2482            let message = response.content().to_string();
2483            Err(Error::InvalidArguments(message))
2484        } else {
2485            Err(Error::Protocol(format!(
2486                "USEFEATURE provided an invalid response code: {}",
2487                response.status_code
2488            )))
2489        }
2490    }
2491
2492    /// Gets the addresses and ports where Tor is listening for connections.
2493    ///
2494    /// Returns a list of (address, port) tuples for the specified listener type.
2495    ///
2496    /// # Arguments
2497    ///
2498    /// * `listener_type` - The type of listener to query
2499    ///
2500    /// # Errors
2501    ///
2502    /// Returns [`Error::OperationFailed`] if:
2503    /// - The GETINFO command fails
2504    ///
2505    /// Returns [`Error::Protocol`] if:
2506    /// - The response format is unexpected
2507    ///
2508    /// # Example
2509    ///
2510    /// ```rust,no_run
2511    /// use stem_rs::controller::{Controller, ListenerType};
2512    ///
2513    /// # async fn example() -> Result<(), stem_rs::Error> {
2514    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2515    /// controller.authenticate(None).await?;
2516    ///
2517    /// let listeners = controller.get_listeners(ListenerType::Socks).await?;
2518    /// for (addr, port) in listeners {
2519    ///     println!("SOCKS listener: {}:{}", addr, port);
2520    /// }
2521    /// # Ok(())
2522    /// # }
2523    /// ```
2524    ///
2525    /// # See Also
2526    ///
2527    /// - [`get_ports`](Self::get_ports): Get just the port numbers
2528    pub async fn get_listeners(
2529        &mut self,
2530        listener_type: ListenerType,
2531    ) -> Result<Vec<(String, u16)>, Error> {
2532        let query = format!("net/listeners/{}", listener_type);
2533        let response = self.get_info(&query).await?;
2534
2535        parse_listeners(&response)
2536    }
2537
2538    /// Gets just the port numbers where Tor is listening for connections.
2539    ///
2540    /// Returns a set of unique port numbers for the specified listener type.
2541    /// This is a convenience method that extracts just the ports from
2542    /// [`get_listeners`](Self::get_listeners).
2543    ///
2544    /// # Arguments
2545    ///
2546    /// * `listener_type` - The type of listener to query
2547    ///
2548    /// # Example
2549    ///
2550    /// ```rust,no_run
2551    /// use stem_rs::controller::{Controller, ListenerType};
2552    ///
2553    /// # async fn example() -> Result<(), stem_rs::Error> {
2554    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2555    /// controller.authenticate(None).await?;
2556    ///
2557    /// let ports = controller.get_ports(ListenerType::Socks).await?;
2558    /// for port in ports {
2559    ///     println!("SOCKS port: {}", port);
2560    /// }
2561    /// # Ok(())
2562    /// # }
2563    /// ```
2564    ///
2565    /// # See Also
2566    ///
2567    /// - [`get_listeners`](Self::get_listeners): Get addresses and ports
2568    pub async fn get_ports(&mut self, listener_type: ListenerType) -> Result<HashSet<u16>, Error> {
2569        let listeners = self.get_listeners(listener_type).await?;
2570        Ok(listeners.into_iter().map(|(_, port)| port).collect())
2571    }
2572
2573    /// Gets the user Tor is running as.
2574    ///
2575    /// # Errors
2576    ///
2577    /// Returns [`Error::OperationFailed`] if:
2578    /// - The information is not available
2579    /// - Tor returns an error response
2580    ///
2581    /// # Example
2582    ///
2583    /// ```rust,no_run
2584    /// use stem_rs::controller::Controller;
2585    ///
2586    /// # async fn example() -> Result<(), stem_rs::Error> {
2587    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2588    /// controller.authenticate(None).await?;
2589    ///
2590    /// let user = controller.get_user().await?;
2591    /// println!("Tor is running as: {}", user);
2592    /// # Ok(())
2593    /// # }
2594    /// ```
2595    pub async fn get_user(&mut self) -> Result<String, Error> {
2596        self.get_info("process/user").await
2597    }
2598
2599    /// Gets the Unix timestamp when Tor started.
2600    ///
2601    /// Calculates the start time by subtracting the uptime from the current time.
2602    ///
2603    /// # Errors
2604    ///
2605    /// Returns an error if:
2606    /// - The uptime cannot be determined
2607    /// - The uptime value is invalid
2608    ///
2609    /// # Example
2610    ///
2611    /// ```rust,no_run
2612    /// use stem_rs::controller::Controller;
2613    ///
2614    /// # async fn example() -> Result<(), stem_rs::Error> {
2615    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2616    /// controller.authenticate(None).await?;
2617    ///
2618    /// let start_time = controller.get_start_time().await?;
2619    /// println!("Tor started at: {}", start_time);
2620    /// # Ok(())
2621    /// # }
2622    /// ```
2623    ///
2624    /// # See Also
2625    ///
2626    /// - [`get_uptime`](Self::get_uptime): Get how long Tor has been running
2627    pub async fn get_start_time(&mut self) -> Result<f64, Error> {
2628        let uptime = self.get_uptime().await?;
2629        let now = SystemTime::now()
2630            .duration_since(UNIX_EPOCH)
2631            .map_err(|e| Error::Parse {
2632                location: "system time".to_string(),
2633                reason: e.to_string(),
2634            })?
2635            .as_secs_f64();
2636        Ok(now - uptime)
2637    }
2638
2639    /// Gets how long Tor has been running in seconds.
2640    ///
2641    /// # Errors
2642    ///
2643    /// Returns an error if:
2644    /// - The GETINFO command fails
2645    /// - The uptime value cannot be parsed
2646    ///
2647    /// # Example
2648    ///
2649    /// ```rust,no_run
2650    /// use stem_rs::controller::Controller;
2651    ///
2652    /// # async fn example() -> Result<(), stem_rs::Error> {
2653    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2654    /// controller.authenticate(None).await?;
2655    ///
2656    /// let uptime = controller.get_uptime().await?;
2657    /// println!("Tor has been running for {} seconds", uptime);
2658    /// # Ok(())
2659    /// # }
2660    /// ```
2661    ///
2662    /// # See Also
2663    ///
2664    /// - [`get_start_time`](Self::get_start_time): Get when Tor started
2665    pub async fn get_uptime(&mut self) -> Result<f64, Error> {
2666        let uptime_str = self.get_info("process/uptime").await?;
2667        uptime_str.parse().map_err(|_| Error::Parse {
2668            location: "uptime".to_string(),
2669            reason: format!("invalid uptime value: {}", uptime_str),
2670        })
2671    }
2672
2673    /// Gets protocol information including authentication methods.
2674    ///
2675    /// Returns information about the Tor control protocol version,
2676    /// the Tor version, and available authentication methods.
2677    ///
2678    /// # Errors
2679    ///
2680    /// Returns [`Error::Parse`] if:
2681    /// - The PROTOCOLINFO response cannot be parsed
2682    ///
2683    /// # Example
2684    ///
2685    /// ```rust,no_run
2686    /// use stem_rs::controller::Controller;
2687    ///
2688    /// # async fn example() -> Result<(), stem_rs::Error> {
2689    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2690    ///
2691    /// let info = controller.get_protocolinfo().await?;
2692    /// println!("Protocol version: {}", info.protocol_version);
2693    /// println!("Tor version: {}", info.tor_version);
2694    /// println!("Auth methods: {:?}", info.auth_methods);
2695    /// # Ok(())
2696    /// # }
2697    /// ```
2698    pub async fn get_protocolinfo(&mut self) -> Result<ProtocolInfo, Error> {
2699        self.socket.send("PROTOCOLINFO 1").await?;
2700        let response = self.recv_response().await?;
2701
2702        if !response.is_ok() {
2703            return Err(Error::OperationFailed {
2704                code: response.status_code.to_string(),
2705                message: response.content().to_string(),
2706            });
2707        }
2708
2709        parse_protocolinfo(&response.all_content())
2710    }
2711
2712    /// Gets accounting statistics for bandwidth limiting.
2713    ///
2714    /// Returns statistics about Tor's accounting status when AccountingMax
2715    /// is set in the torrc.
2716    ///
2717    /// # Errors
2718    ///
2719    /// Returns [`Error::OperationFailed`] if:
2720    /// - Accounting is not enabled
2721    /// - The GETINFO commands fail
2722    ///
2723    /// # Example
2724    ///
2725    /// ```rust,no_run
2726    /// use stem_rs::controller::Controller;
2727    ///
2728    /// # async fn example() -> Result<(), stem_rs::Error> {
2729    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2730    /// controller.authenticate(None).await?;
2731    ///
2732    /// let stats = controller.get_accounting_stats().await?;
2733    /// println!("Status: {}", stats.status);
2734    /// println!("Read: {} bytes", stats.read_bytes);
2735    /// println!("Written: {} bytes", stats.written_bytes);
2736    /// # Ok(())
2737    /// # }
2738    /// ```
2739    pub async fn get_accounting_stats(&mut self) -> Result<AccountingStats, Error> {
2740        // Check if accounting is enabled
2741        let enabled = self.get_info("accounting/enabled").await?;
2742        if enabled != "1" {
2743            return Err(Error::OperationFailed {
2744                code: "552".to_string(),
2745                message: "Accounting isn't enabled".to_string(),
2746            });
2747        }
2748
2749        let retrieved = SystemTime::now()
2750            .duration_since(UNIX_EPOCH)
2751            .map_err(|e| Error::Parse {
2752                location: "system time".to_string(),
2753                reason: e.to_string(),
2754            })?
2755            .as_secs_f64();
2756
2757        let status = self.get_info("accounting/hibernating").await?;
2758        let interval_end = self.get_info("accounting/interval-end").await.ok();
2759        let bytes = self.get_info("accounting/bytes").await?;
2760        let bytes_left = self.get_info("accounting/bytes-left").await?;
2761
2762        // Parse bytes: "read_bytes written_bytes"
2763        let (read_bytes, written_bytes) = parse_accounting_bytes(&bytes)?;
2764        let (read_bytes_left, write_bytes_left) = parse_accounting_bytes(&bytes_left)?;
2765
2766        // Calculate time until reset
2767        let time_until_reset = if let Some(ref end) = interval_end {
2768            parse_interval_end_to_seconds(end, retrieved).unwrap_or(0)
2769        } else {
2770            0
2771        };
2772
2773        Ok(AccountingStats {
2774            retrieved,
2775            status,
2776            interval_end,
2777            time_until_reset,
2778            read_bytes,
2779            read_bytes_left,
2780            read_limit: read_bytes + read_bytes_left,
2781            written_bytes,
2782            write_bytes_left,
2783            write_limit: written_bytes + write_bytes_left,
2784        })
2785    }
2786
2787    /// Retrieves and parses the current network status consensus document.
2788    ///
2789    /// The consensus document contains the agreed-upon view of the Tor network,
2790    /// including information about all known relays, their flags, and bandwidth
2791    /// weights. This is the authoritative source for network topology.
2792    ///
2793    /// # Returns
2794    ///
2795    /// Returns the parsed [`NetworkStatusDocument`](crate::descriptor::NetworkStatusDocument)
2796    /// containing all consensus information.
2797    ///
2798    /// # Errors
2799    ///
2800    /// Returns an error if:
2801    /// - The GETINFO command fails
2802    /// - The consensus document cannot be parsed
2803    /// - The connection to Tor is lost
2804    ///
2805    /// # Example
2806    ///
2807    /// ```rust,no_run
2808    /// use stem_rs::controller::Controller;
2809    ///
2810    /// # async fn example() -> Result<(), stem_rs::Error> {
2811    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2812    /// controller.authenticate(None).await?;
2813    ///
2814    /// let consensus = controller.get_consensus().await?;
2815    /// println!("Consensus valid from {} to {}",
2816    ///          consensus.valid_after, consensus.valid_until);
2817    /// # Ok(())
2818    /// # }
2819    /// ```
2820    #[cfg(feature = "descriptors")]
2821    pub async fn get_consensus(
2822        &mut self,
2823    ) -> Result<crate::descriptor::NetworkStatusDocument, Error> {
2824        use crate::descriptor::Descriptor;
2825
2826        // Check cache first
2827        if let Some(cache) = &self.descriptor_cache {
2828            if let Some(consensus) = cache.get_consensus() {
2829                return Ok(consensus);
2830            }
2831        }
2832
2833        // Cache miss - fetch from Tor
2834        let consensus_text = self.get_info("dir/status-vote/current/consensus").await?;
2835        let consensus = crate::descriptor::NetworkStatusDocument::parse(&consensus_text)?;
2836
2837        // Store in cache
2838        if let Some(cache) = &self.descriptor_cache {
2839            cache.put_consensus(consensus.clone());
2840        }
2841
2842        Ok(consensus)
2843    }
2844
2845    /// Retrieves and parses a server descriptor for a specific relay.
2846    ///
2847    /// Server descriptors contain full relay metadata including identity keys,
2848    /// exit policies, bandwidth information, and platform details.
2849    ///
2850    /// # Arguments
2851    ///
2852    /// * `fingerprint` - The relay's fingerprint (40 hex characters)
2853    ///
2854    /// # Returns
2855    ///
2856    /// Returns the parsed [`ServerDescriptor`](crate::descriptor::ServerDescriptor)
2857    /// for the specified relay.
2858    ///
2859    /// # Errors
2860    ///
2861    /// Returns an error if:
2862    /// - The fingerprint is invalid
2863    /// - The descriptor is not available
2864    /// - The descriptor cannot be parsed
2865    ///
2866    /// # Example
2867    ///
2868    /// ```rust,no_run
2869    /// use stem_rs::controller::Controller;
2870    ///
2871    /// # async fn example() -> Result<(), stem_rs::Error> {
2872    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2873    /// controller.authenticate(None).await?;
2874    ///
2875    /// let fingerprint = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
2876    /// let descriptor = controller.get_server_descriptor(fingerprint).await?;
2877    /// println!("Relay: {} at {}", descriptor.nickname, descriptor.address);
2878    /// # Ok(())
2879    /// # }
2880    /// ```
2881    #[cfg(feature = "descriptors")]
2882    pub async fn get_server_descriptor(
2883        &mut self,
2884        fingerprint: &str,
2885    ) -> Result<crate::descriptor::ServerDescriptor, Error> {
2886        use crate::descriptor::Descriptor;
2887
2888        // Check cache first
2889        if let Some(cache) = &self.descriptor_cache {
2890            if let Some(descriptor) = cache.get_server_descriptor(fingerprint) {
2891                return Ok(descriptor);
2892            }
2893        }
2894
2895        // Cache miss - fetch from Tor
2896        let key = format!("desc/id/{}", fingerprint);
2897        let descriptor_text = self.get_info(&key).await?;
2898        let descriptor = crate::descriptor::ServerDescriptor::parse(&descriptor_text)?;
2899
2900        // Store in cache
2901        if let Some(cache) = &self.descriptor_cache {
2902            cache.put_server_descriptor(fingerprint.to_string(), descriptor.clone());
2903        }
2904
2905        Ok(descriptor)
2906    }
2907
2908    /// Retrieves and parses a microdescriptor for a specific relay.
2909    ///
2910    /// Microdescriptors are compact descriptors used by clients for building
2911    /// circuits with minimal bandwidth overhead. They contain only essential
2912    /// routing information.
2913    ///
2914    /// # Arguments
2915    ///
2916    /// * `digest` - The microdescriptor digest (base64 or hex encoded)
2917    ///
2918    /// # Returns
2919    ///
2920    /// Returns the parsed [`Microdescriptor`](crate::descriptor::Microdescriptor)
2921    /// for the specified digest.
2922    ///
2923    /// # Errors
2924    ///
2925    /// Returns an error if:
2926    /// - The digest is invalid
2927    /// - The microdescriptor is not available
2928    /// - The microdescriptor cannot be parsed
2929    ///
2930    /// # Example
2931    ///
2932    /// ```rust,no_run
2933    /// use stem_rs::controller::Controller;
2934    ///
2935    /// # async fn example() -> Result<(), stem_rs::Error> {
2936    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2937    /// controller.authenticate(None).await?;
2938    ///
2939    /// let digest = "abcdef1234567890";
2940    /// let microdesc = controller.get_microdescriptor(digest).await?;
2941    /// println!("Microdescriptor onion key: {}", microdesc.onion_key);
2942    /// # Ok(())
2943    /// # }
2944    /// ```
2945    #[cfg(feature = "descriptors")]
2946    pub async fn get_microdescriptor(
2947        &mut self,
2948        digest: &str,
2949    ) -> Result<crate::descriptor::Microdescriptor, Error> {
2950        use crate::descriptor::Descriptor;
2951
2952        // Check cache first
2953        if let Some(cache) = &self.descriptor_cache {
2954            if let Some(descriptor) = cache.get_microdescriptor(digest) {
2955                return Ok(descriptor);
2956            }
2957        }
2958
2959        // Cache miss - fetch from Tor
2960        let key = format!("md/id/{}", digest);
2961        let descriptor_text = self.get_info(&key).await?;
2962        let descriptor = crate::descriptor::Microdescriptor::parse(&descriptor_text)?;
2963
2964        // Store in cache
2965        if let Some(cache) = &self.descriptor_cache {
2966            cache.put_microdescriptor(digest.to_string(), descriptor.clone());
2967        }
2968
2969        Ok(descriptor)
2970    }
2971
2972    /// Retrieves and parses all router status entries from the network.
2973    ///
2974    /// Router status entries are compact representations of relays in the
2975    /// consensus, containing essential information like nickname, fingerprint,
2976    /// flags, and bandwidth.
2977    ///
2978    /// # Returns
2979    ///
2980    /// Returns a vector of [`RouterStatusEntry`](crate::descriptor::RouterStatusEntry)
2981    /// objects, one for each relay in the network.
2982    ///
2983    /// # Errors
2984    ///
2985    /// Returns an error if:
2986    /// - The GETINFO command fails
2987    /// - The router status entries cannot be parsed
2988    ///
2989    /// # Example
2990    ///
2991    /// ```rust,no_run
2992    /// use stem_rs::controller::Controller;
2993    ///
2994    /// # async fn example() -> Result<(), stem_rs::Error> {
2995    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2996    /// controller.authenticate(None).await?;
2997    ///
2998    /// let entries = controller.get_router_status_entries().await?;
2999    /// println!("Found {} relays in the network", entries.len());
3000    /// # Ok(())
3001    /// # }
3002    /// ```
3003    #[cfg(feature = "descriptors")]
3004    pub async fn get_router_status_entries(
3005        &mut self,
3006    ) -> Result<Vec<crate::descriptor::RouterStatusEntry>, Error> {
3007        use crate::descriptor::RouterStatusEntry;
3008
3009        let ns_text = self.get_info("ns/all").await?;
3010
3011        let mut entries = Vec::new();
3012        let mut current_entry = String::new();
3013
3014        for line in ns_text.lines() {
3015            if line.starts_with("r ") && !current_entry.is_empty() {
3016                if let Ok(entry) = RouterStatusEntry::parse(&current_entry) {
3017                    entries.push(entry);
3018                }
3019                current_entry.clear();
3020            }
3021            current_entry.push_str(line);
3022            current_entry.push('\n');
3023        }
3024
3025        if !current_entry.is_empty() {
3026            if let Ok(entry) = RouterStatusEntry::parse(&current_entry) {
3027                entries.push(entry);
3028            }
3029        }
3030
3031        Ok(entries)
3032    }
3033
3034    /// Finds all relays with a specific flag.
3035    ///
3036    /// Filters the network to return only relays that have been assigned
3037    /// the specified flag by directory authorities. Common flags include
3038    /// Guard, Exit, Fast, Stable, and Running.
3039    ///
3040    /// # Arguments
3041    ///
3042    /// * `flag` - The flag to filter by (e.g., [`Flag::Guard`](crate::Flag::Guard))
3043    ///
3044    /// # Returns
3045    ///
3046    /// Returns a vector of [`RouterStatusEntry`](crate::descriptor::RouterStatusEntry)
3047    /// objects for relays with the specified flag.
3048    ///
3049    /// # Errors
3050    ///
3051    /// Returns an error if the router status entries cannot be retrieved.
3052    ///
3053    /// # Example
3054    ///
3055    /// ```rust,no_run
3056    /// use stem_rs::controller::Controller;
3057    /// use stem_rs::Flag;
3058    ///
3059    /// # async fn example() -> Result<(), stem_rs::Error> {
3060    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
3061    /// controller.authenticate(None).await?;
3062    ///
3063    /// let guard_relays = controller.find_relays_by_flag(Flag::Guard).await?;
3064    /// println!("Found {} guard relays", guard_relays.len());
3065    /// # Ok(())
3066    /// # }
3067    /// ```
3068    #[cfg(feature = "descriptors")]
3069    pub async fn find_relays_by_flag(
3070        &mut self,
3071        flag: crate::Flag,
3072    ) -> Result<Vec<crate::descriptor::RouterStatusEntry>, Error> {
3073        let entries = self.get_router_status_entries().await?;
3074
3075        let flag_str = flag.to_string();
3076        let matching_relays = entries
3077            .into_iter()
3078            .filter(|entry| entry.flags.contains(&flag_str))
3079            .collect();
3080
3081        Ok(matching_relays)
3082    }
3083
3084    /// Finds the fastest relays in the network by bandwidth.
3085    ///
3086    /// Returns the top N relays sorted by consensus bandwidth weight.
3087    /// This is useful for selecting high-performance relays for circuits.
3088    ///
3089    /// # Arguments
3090    ///
3091    /// * `count` - Maximum number of relays to return
3092    ///
3093    /// # Returns
3094    ///
3095    /// Returns a vector of up to `count` [`RouterStatusEntry`](crate::descriptor::RouterStatusEntry)
3096    /// objects, sorted by bandwidth (highest first).
3097    ///
3098    /// # Errors
3099    ///
3100    /// Returns an error if the router status entries cannot be retrieved.
3101    ///
3102    /// # Example
3103    ///
3104    /// ```rust,no_run
3105    /// use stem_rs::controller::Controller;
3106    ///
3107    /// # async fn example() -> Result<(), stem_rs::Error> {
3108    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
3109    /// controller.authenticate(None).await?;
3110    ///
3111    /// let fastest = controller.find_fastest_relays(10).await?;
3112    /// for (i, relay) in fastest.iter().enumerate() {
3113    ///     println!("#{}: {} - {} KB/s",
3114    ///              i + 1, relay.nickname, relay.bandwidth.unwrap_or(0));
3115    /// }
3116    /// # Ok(())
3117    /// # }
3118    /// ```
3119    #[cfg(feature = "descriptors")]
3120    pub async fn find_fastest_relays(
3121        &mut self,
3122        count: usize,
3123    ) -> Result<Vec<crate::descriptor::RouterStatusEntry>, Error> {
3124        let mut entries = self.get_router_status_entries().await?;
3125
3126        entries.sort_by(|a, b| {
3127            let a_bw = a.bandwidth.unwrap_or(0);
3128            let b_bw = b.bandwidth.unwrap_or(0);
3129            b_bw.cmp(&a_bw)
3130        });
3131
3132        entries.truncate(count);
3133        Ok(entries)
3134    }
3135
3136    /// Selects a guard relay using bandwidth-weighted random selection.
3137    ///
3138    /// Implements the Tor path selection algorithm for choosing guard relays.
3139    /// Relays with higher bandwidth have proportionally higher probability
3140    /// of being selected. This mimics how Tor clients select guards.
3141    ///
3142    /// # Returns
3143    ///
3144    /// Returns `Some(RouterStatusEntry)` if a guard relay was selected,
3145    /// or `None` if no guard relays are available.
3146    ///
3147    /// # Errors
3148    ///
3149    /// Returns an error if:
3150    /// - The router status entries cannot be retrieved
3151    /// - Random number generation fails
3152    ///
3153    /// # Example
3154    ///
3155    /// ```rust,no_run
3156    /// use stem_rs::controller::Controller;
3157    ///
3158    /// # async fn example() -> Result<(), stem_rs::Error> {
3159    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
3160    /// controller.authenticate(None).await?;
3161    ///
3162    /// if let Some(guard) = controller.select_guard_relay().await? {
3163    ///     println!("Selected guard: {} ({})", guard.nickname, guard.fingerprint);
3164    /// }
3165    /// # Ok(())
3166    /// # }
3167    /// ```
3168    #[cfg(feature = "descriptors")]
3169    pub async fn select_guard_relay(
3170        &mut self,
3171    ) -> Result<Option<crate::descriptor::RouterStatusEntry>, Error> {
3172        use crate::Flag;
3173
3174        let guard_relays = self.find_relays_by_flag(Flag::Guard).await?;
3175
3176        if guard_relays.is_empty() {
3177            return Ok(None);
3178        }
3179
3180        let total_bandwidth: u64 = guard_relays.iter().map(|r| r.bandwidth.unwrap_or(0)).sum();
3181
3182        if total_bandwidth == 0 {
3183            return Ok(guard_relays.into_iter().next());
3184        }
3185
3186        let mut random_bytes = [0u8; 8];
3187        use rand::RngCore;
3188        let mut rng = rand::rng();
3189        rng.fill_bytes(&mut random_bytes);
3190        let random_value = u64::from_le_bytes(random_bytes) % total_bandwidth;
3191
3192        let mut cumulative = 0u64;
3193        for relay in guard_relays {
3194            cumulative += relay.bandwidth.unwrap_or(0);
3195            if cumulative > random_value {
3196                return Ok(Some(relay));
3197            }
3198        }
3199
3200        Ok(None)
3201    }
3202}
3203
3204/// Response from ADD_ONION command.
3205///
3206/// Contains the service ID and optionally the private key for the hidden service.
3207#[derive(Debug, Clone)]
3208pub struct AddOnionResponse {
3209    /// The onion address without the `.onion` suffix.
3210    pub service_id: String,
3211    /// The private key (base64 encoded), if not discarded.
3212    pub private_key: Option<String>,
3213    /// The type of private key (e.g., `"ED25519-V3"`, `"RSA1024"`).
3214    pub private_key_type: Option<String>,
3215}
3216
3217/// Parses the response from an ADD_ONION command.
3218fn parse_add_onion_response(content: &str) -> Result<AddOnionResponse, Error> {
3219    let mut service_id = None;
3220    let mut private_key = None;
3221    let mut private_key_type = None;
3222
3223    for line in content.lines() {
3224        let line = line.trim();
3225        if let Some(value) = line.strip_prefix("ServiceID=") {
3226            service_id = Some(value.to_string());
3227        } else if let Some(value) = line.strip_prefix("PrivateKey=") {
3228            if let Some((key_type, key_content)) = value.split_once(':') {
3229                private_key_type = Some(key_type.to_string());
3230                private_key = Some(key_content.to_string());
3231            }
3232        }
3233    }
3234
3235    let service_id = service_id.ok_or_else(|| Error::Parse {
3236        location: "ADD_ONION response".to_string(),
3237        reason: "missing ServiceID".to_string(),
3238    })?;
3239
3240    Ok(AddOnionResponse {
3241        service_id,
3242        private_key,
3243        private_key_type,
3244    })
3245}
3246
3247/// Parses circuit status output from GETINFO circuit-status.
3248///
3249/// Converts the multi-line circuit status response into a vector of [`Circuit`] structs.
3250fn parse_circuits(content: &str) -> Result<Vec<Circuit>, Error> {
3251    let mut circuits = Vec::new();
3252
3253    for line in content.lines() {
3254        let line = line.trim();
3255        if line.is_empty() {
3256            continue;
3257        }
3258
3259        let mut parts = line.split_whitespace();
3260        let id = parts.next().ok_or_else(|| Error::Parse {
3261            location: "circuit".to_string(),
3262            reason: "missing circuit id".to_string(),
3263        })?;
3264
3265        let status_str = parts.next().ok_or_else(|| Error::Parse {
3266            location: "circuit".to_string(),
3267            reason: "missing circuit status".to_string(),
3268        })?;
3269
3270        let status = parse_circ_status(status_str)?;
3271
3272        let mut path = Vec::new();
3273        if let Some(path_str) = parts.next() {
3274            if !path_str.starts_with("BUILD_FLAGS=")
3275                && !path_str.starts_with("PURPOSE=")
3276                && !path_str.starts_with("TIME_CREATED=")
3277            {
3278                for relay in path_str.split(',') {
3279                    let relay_info = parse_relay_info(relay);
3280                    path.push(relay_info);
3281                }
3282            }
3283        }
3284
3285        circuits.push(Circuit {
3286            id: CircuitId::new(id),
3287            status,
3288            path,
3289        });
3290    }
3291
3292    Ok(circuits)
3293}
3294
3295/// Parses a circuit status string into a [`CircStatus`] enum.
3296fn parse_circ_status(s: &str) -> Result<CircStatus, Error> {
3297    match s.to_uppercase().as_str() {
3298        "LAUNCHED" => Ok(CircStatus::Launched),
3299        "BUILT" => Ok(CircStatus::Built),
3300        "GUARD_WAIT" => Ok(CircStatus::GuardWait),
3301        "EXTENDED" => Ok(CircStatus::Extended),
3302        "FAILED" => Ok(CircStatus::Failed),
3303        "CLOSED" => Ok(CircStatus::Closed),
3304        _ => Err(Error::Parse {
3305            location: "circuit status".to_string(),
3306            reason: format!("unknown status: {}", s),
3307        }),
3308    }
3309}
3310
3311/// Parses a relay specification string into a [`RelayInfo`] struct.
3312///
3313/// Handles formats like `$FINGERPRINT~Nickname` or just `$FINGERPRINT`.
3314fn parse_relay_info(s: &str) -> RelayInfo {
3315    if let Some((fingerprint, nickname)) = s.split_once('~') {
3316        RelayInfo {
3317            fingerprint: fingerprint.trim_start_matches('$').to_string(),
3318            nickname: Some(nickname.to_string()),
3319        }
3320    } else {
3321        RelayInfo {
3322            fingerprint: s.trim_start_matches('$').to_string(),
3323            nickname: None,
3324        }
3325    }
3326}
3327
3328/// Parses stream status output from GETINFO stream-status.
3329///
3330/// Converts the multi-line stream status response into a vector of [`Stream`] structs.
3331fn parse_streams(content: &str) -> Result<Vec<Stream>, Error> {
3332    let mut streams = Vec::new();
3333
3334    for line in content.lines() {
3335        let line = line.trim();
3336        if line.is_empty() {
3337            continue;
3338        }
3339
3340        let mut parts = line.split_whitespace();
3341        let id = parts.next().ok_or_else(|| Error::Parse {
3342            location: "stream".to_string(),
3343            reason: "missing stream id".to_string(),
3344        })?;
3345
3346        let status_str = parts.next().ok_or_else(|| Error::Parse {
3347            location: "stream".to_string(),
3348            reason: "missing stream status".to_string(),
3349        })?;
3350
3351        let status = parse_stream_status(status_str)?;
3352
3353        let circuit_id_str = parts.next().ok_or_else(|| Error::Parse {
3354            location: "stream".to_string(),
3355            reason: "missing circuit id".to_string(),
3356        })?;
3357
3358        let circuit_id = if circuit_id_str == "0" {
3359            None
3360        } else {
3361            Some(CircuitId::new(circuit_id_str))
3362        };
3363
3364        let target = parts.next().ok_or_else(|| Error::Parse {
3365            location: "stream".to_string(),
3366            reason: "missing target".to_string(),
3367        })?;
3368
3369        let (target_host, target_port) = parse_target(target)?;
3370
3371        streams.push(Stream {
3372            id: StreamId::new(id),
3373            status,
3374            circuit_id,
3375            target_host,
3376            target_port,
3377        });
3378    }
3379
3380    Ok(streams)
3381}
3382
3383/// Parses a stream status string into a [`StreamStatus`] enum.
3384fn parse_stream_status(s: &str) -> Result<StreamStatus, Error> {
3385    match s.to_uppercase().as_str() {
3386        "NEW" => Ok(StreamStatus::New),
3387        "NEWRESOLVE" => Ok(StreamStatus::NewResolve),
3388        "REMAP" => Ok(StreamStatus::Remap),
3389        "SENTCONNECT" => Ok(StreamStatus::SentConnect),
3390        "SENTRESOLVE" => Ok(StreamStatus::SentResolve),
3391        "SUCCEEDED" => Ok(StreamStatus::Succeeded),
3392        "FAILED" => Ok(StreamStatus::Failed),
3393        "DETACHED" => Ok(StreamStatus::Detached),
3394        "CONTROLLER_WAIT" => Ok(StreamStatus::ControllerWait),
3395        "CLOSED" => Ok(StreamStatus::Closed),
3396        _ => Err(Error::Parse {
3397            location: "stream status".to_string(),
3398            reason: format!("unknown status: {}", s),
3399        }),
3400    }
3401}
3402
3403/// Parses a target address string into host and port components.
3404///
3405/// Handles formats like `host:port` or just `host` (port defaults to 0).
3406fn parse_target(target: &str) -> Result<(String, u16), Error> {
3407    if let Some(colon_pos) = target.rfind(':') {
3408        let host = target[..colon_pos].to_string();
3409        let port_str = &target[colon_pos + 1..];
3410        let port: u16 = port_str.parse().map_err(|_| Error::Parse {
3411            location: "stream target".to_string(),
3412            reason: format!("invalid port: {}", port_str),
3413        })?;
3414        Ok((host, port))
3415    } else {
3416        Ok((target.to_string(), 0))
3417    }
3418}
3419
3420/// Parses listener response from GETINFO net/listeners/*.
3421///
3422/// The response contains quoted "address:port" pairs separated by spaces.
3423fn parse_listeners(response: &str) -> Result<Vec<(String, u16)>, Error> {
3424    let mut listeners = Vec::new();
3425
3426    for listener in response.split_whitespace() {
3427        // Strip quotes
3428        let listener = listener.trim_matches('"');
3429        if listener.is_empty() {
3430            continue;
3431        }
3432
3433        // Skip unix sockets
3434        if listener.starts_with("unix:") {
3435            continue;
3436        }
3437
3438        // Find the last colon to split address and port
3439        if let Some(colon_pos) = listener.rfind(':') {
3440            let mut addr = listener[..colon_pos].to_string();
3441            let port_str = &listener[colon_pos + 1..];
3442
3443            // Handle IPv6 addresses in brackets
3444            if addr.starts_with('[') && addr.ends_with(']') {
3445                addr = addr[1..addr.len() - 1].to_string();
3446            }
3447
3448            let port: u16 = port_str.parse().map_err(|_| Error::Parse {
3449                location: "listener".to_string(),
3450                reason: format!("invalid port: {}", port_str),
3451            })?;
3452
3453            listeners.push((addr, port));
3454        }
3455    }
3456
3457    Ok(listeners)
3458}
3459
3460/// Parses PROTOCOLINFO response.
3461fn parse_protocolinfo(content: &str) -> Result<ProtocolInfo, Error> {
3462    let mut protocol_version = 1;
3463    let mut tor_version = String::new();
3464    let mut auth_methods = Vec::new();
3465    let mut cookie_file = None;
3466
3467    for line in content.lines() {
3468        let line = line.trim();
3469
3470        if let Some(stripped) = line.strip_prefix("PROTOCOLINFO ") {
3471            if let Ok(v) = stripped.trim().parse::<u32>() {
3472                protocol_version = v;
3473            }
3474        } else if let Some(rest) = line.strip_prefix("AUTH METHODS=") {
3475            // Parse: AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE="/path/to/cookie"
3476
3477            // Find METHODS value
3478            if let Some(space_pos) = rest.find(' ') {
3479                let methods_str = &rest[..space_pos];
3480                auth_methods = methods_str.split(',').map(|s| s.to_string()).collect();
3481
3482                // Look for COOKIEFILE
3483                let remaining = &rest[space_pos..];
3484                if let Some(cookie_start) = remaining.find("COOKIEFILE=\"") {
3485                    let cookie_path_start = cookie_start + 12;
3486                    if let Some(cookie_end) = remaining[cookie_path_start..].find('"') {
3487                        cookie_file = Some(
3488                            remaining[cookie_path_start..cookie_path_start + cookie_end]
3489                                .to_string(),
3490                        );
3491                    }
3492                }
3493            } else {
3494                auth_methods = rest.split(',').map(|s| s.to_string()).collect();
3495            }
3496        } else if line.starts_with("VERSION Tor=\"") {
3497            // Parse: VERSION Tor="0.4.7.10"
3498            if let Some(start) = line.find("Tor=\"") {
3499                let version_start = start + 5;
3500                if let Some(end) = line[version_start..].find('"') {
3501                    tor_version = line[version_start..version_start + end].to_string();
3502                }
3503            }
3504        }
3505    }
3506
3507    Ok(ProtocolInfo {
3508        protocol_version,
3509        tor_version,
3510        auth_methods,
3511        cookie_file,
3512    })
3513}
3514
3515/// Parses accounting bytes response: "read_bytes written_bytes"
3516fn parse_accounting_bytes(bytes_str: &str) -> Result<(u64, u64), Error> {
3517    let parts: Vec<&str> = bytes_str.split_whitespace().collect();
3518    if parts.len() != 2 {
3519        return Err(Error::Parse {
3520            location: "accounting bytes".to_string(),
3521            reason: format!("expected 'read written', got: {}", bytes_str),
3522        });
3523    }
3524
3525    let read: u64 = parts[0].parse().map_err(|_| Error::Parse {
3526        location: "accounting bytes".to_string(),
3527        reason: format!("invalid read bytes: {}", parts[0]),
3528    })?;
3529
3530    let written: u64 = parts[1].parse().map_err(|_| Error::Parse {
3531        location: "accounting bytes".to_string(),
3532        reason: format!("invalid written bytes: {}", parts[1]),
3533    })?;
3534
3535    Ok((read, written))
3536}
3537
3538/// Parses interval end timestamp and calculates seconds until reset.
3539fn parse_interval_end_to_seconds(interval_end: &str, current_time: f64) -> Option<u64> {
3540    // interval_end format: "YYYY-MM-DD HH:MM:SS"
3541    use chrono::NaiveDateTime;
3542
3543    let naive_dt = NaiveDateTime::parse_from_str(interval_end, "%Y-%m-%d %H:%M:%S").ok()?;
3544    let end_timestamp = naive_dt.and_utc().timestamp() as f64;
3545
3546    if end_timestamp > current_time {
3547        Some((end_timestamp - current_time) as u64)
3548    } else {
3549        Some(0)
3550    }
3551}
3552
3553#[cfg(test)]
3554mod tests {
3555    use super::*;
3556
3557    #[test]
3558    fn test_circuit_id_display() {
3559        let id = CircuitId::new("123");
3560        assert_eq!(id.to_string(), "123");
3561    }
3562
3563    #[test]
3564    fn test_stream_id_display() {
3565        let id = StreamId::new("456");
3566        assert_eq!(id.to_string(), "456");
3567    }
3568
3569    #[test]
3570    fn test_parse_circ_status() {
3571        assert_eq!(parse_circ_status("LAUNCHED").unwrap(), CircStatus::Launched);
3572        assert_eq!(parse_circ_status("BUILT").unwrap(), CircStatus::Built);
3573        assert_eq!(
3574            parse_circ_status("GUARD_WAIT").unwrap(),
3575            CircStatus::GuardWait
3576        );
3577        assert_eq!(parse_circ_status("EXTENDED").unwrap(), CircStatus::Extended);
3578        assert_eq!(parse_circ_status("FAILED").unwrap(), CircStatus::Failed);
3579        assert_eq!(parse_circ_status("CLOSED").unwrap(), CircStatus::Closed);
3580        assert_eq!(parse_circ_status("launched").unwrap(), CircStatus::Launched);
3581        assert!(parse_circ_status("UNKNOWN").is_err());
3582    }
3583
3584    #[test]
3585    fn test_parse_stream_status() {
3586        assert_eq!(parse_stream_status("NEW").unwrap(), StreamStatus::New);
3587        assert_eq!(
3588            parse_stream_status("NEWRESOLVE").unwrap(),
3589            StreamStatus::NewResolve
3590        );
3591        assert_eq!(parse_stream_status("REMAP").unwrap(), StreamStatus::Remap);
3592        assert_eq!(
3593            parse_stream_status("SENTCONNECT").unwrap(),
3594            StreamStatus::SentConnect
3595        );
3596        assert_eq!(
3597            parse_stream_status("SENTRESOLVE").unwrap(),
3598            StreamStatus::SentResolve
3599        );
3600        assert_eq!(
3601            parse_stream_status("SUCCEEDED").unwrap(),
3602            StreamStatus::Succeeded
3603        );
3604        assert_eq!(parse_stream_status("FAILED").unwrap(), StreamStatus::Failed);
3605        assert_eq!(
3606            parse_stream_status("DETACHED").unwrap(),
3607            StreamStatus::Detached
3608        );
3609        assert_eq!(
3610            parse_stream_status("CONTROLLER_WAIT").unwrap(),
3611            StreamStatus::ControllerWait
3612        );
3613        assert_eq!(parse_stream_status("CLOSED").unwrap(), StreamStatus::Closed);
3614        assert!(parse_stream_status("UNKNOWN").is_err());
3615    }
3616
3617    #[test]
3618    fn test_parse_relay_info_with_nickname() {
3619        let info = parse_relay_info("$ABCD1234~MyRelay");
3620        assert_eq!(info.fingerprint, "ABCD1234");
3621        assert_eq!(info.nickname, Some("MyRelay".to_string()));
3622    }
3623
3624    #[test]
3625    fn test_parse_relay_info_without_nickname() {
3626        let info = parse_relay_info("$ABCD1234");
3627        assert_eq!(info.fingerprint, "ABCD1234");
3628        assert_eq!(info.nickname, None);
3629    }
3630
3631    #[test]
3632    fn test_parse_relay_info_no_dollar() {
3633        let info = parse_relay_info("ABCD1234~MyRelay");
3634        assert_eq!(info.fingerprint, "ABCD1234");
3635        assert_eq!(info.nickname, Some("MyRelay".to_string()));
3636    }
3637
3638    #[test]
3639    fn test_parse_target_with_port() {
3640        let (host, port) = parse_target("example.com:443").unwrap();
3641        assert_eq!(host, "example.com");
3642        assert_eq!(port, 443);
3643    }
3644
3645    #[test]
3646    fn test_parse_target_ipv4_with_port() {
3647        let (host, port) = parse_target("192.168.1.1:80").unwrap();
3648        assert_eq!(host, "192.168.1.1");
3649        assert_eq!(port, 80);
3650    }
3651
3652    #[test]
3653    fn test_parse_target_without_port() {
3654        let (host, port) = parse_target("example.com").unwrap();
3655        assert_eq!(host, "example.com");
3656        assert_eq!(port, 0);
3657    }
3658
3659    #[test]
3660    fn test_parse_circuits_empty() {
3661        let circuits = parse_circuits("").unwrap();
3662        assert!(circuits.is_empty());
3663    }
3664
3665    #[test]
3666    fn test_parse_circuits_single() {
3667        let content = "1 BUILT $AAAA~Guard,$BBBB~Middle,$CCCC~Exit";
3668        let circuits = parse_circuits(content).unwrap();
3669        assert_eq!(circuits.len(), 1);
3670        assert_eq!(circuits[0].id.0, "1");
3671        assert_eq!(circuits[0].status, CircStatus::Built);
3672        assert_eq!(circuits[0].path.len(), 3);
3673        assert_eq!(circuits[0].path[0].fingerprint, "AAAA");
3674        assert_eq!(circuits[0].path[0].nickname, Some("Guard".to_string()));
3675    }
3676
3677    #[test]
3678    fn test_parse_circuits_multiple() {
3679        let content = "1 BUILT $AAAA~Guard,$BBBB~Exit\n2 LAUNCHED\n3 EXTENDED $CCCC~Relay";
3680        let circuits = parse_circuits(content).unwrap();
3681        assert_eq!(circuits.len(), 3);
3682        assert_eq!(circuits[0].status, CircStatus::Built);
3683        assert_eq!(circuits[1].status, CircStatus::Launched);
3684        assert_eq!(circuits[2].status, CircStatus::Extended);
3685    }
3686
3687    #[test]
3688    fn test_parse_circuits_with_flags() {
3689        let content = "1 BUILT $AAAA~Guard BUILD_FLAGS=IS_INTERNAL PURPOSE=GENERAL";
3690        let circuits = parse_circuits(content).unwrap();
3691        assert_eq!(circuits.len(), 1);
3692        assert_eq!(circuits[0].path.len(), 1);
3693    }
3694
3695    #[test]
3696    fn test_parse_streams_empty() {
3697        let streams = parse_streams("").unwrap();
3698        assert!(streams.is_empty());
3699    }
3700
3701    #[test]
3702    fn test_parse_streams_single() {
3703        let content = "1 SUCCEEDED 5 www.example.com:443";
3704        let streams = parse_streams(content).unwrap();
3705        assert_eq!(streams.len(), 1);
3706        assert_eq!(streams[0].id.0, "1");
3707        assert_eq!(streams[0].status, StreamStatus::Succeeded);
3708        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("5")));
3709        assert_eq!(streams[0].target_host, "www.example.com");
3710        assert_eq!(streams[0].target_port, 443);
3711    }
3712
3713    #[test]
3714    fn test_parse_streams_no_circuit() {
3715        let content = "1 NEW 0 www.example.com:80";
3716        let streams = parse_streams(content).unwrap();
3717        assert_eq!(streams.len(), 1);
3718        assert_eq!(streams[0].circuit_id, None);
3719    }
3720
3721    #[test]
3722    fn test_parse_streams_multiple() {
3723        let content = "1 SUCCEEDED 5 www.example.com:443\n2 NEW 0 api.example.com:80";
3724        let streams = parse_streams(content).unwrap();
3725        assert_eq!(streams.len(), 2);
3726    }
3727
3728    #[test]
3729    fn test_circuit_id_equality() {
3730        let id1 = CircuitId::new("123");
3731        let id2 = CircuitId::new("123");
3732        let id3 = CircuitId::new("456");
3733        assert_eq!(id1, id2);
3734        assert_ne!(id1, id3);
3735    }
3736
3737    #[test]
3738    fn test_stream_id_equality() {
3739        let id1 = StreamId::new("123");
3740        let id2 = StreamId::new("123");
3741        let id3 = StreamId::new("456");
3742        assert_eq!(id1, id2);
3743        assert_ne!(id1, id3);
3744    }
3745}
3746
3747#[cfg(test)]
3748mod stem_tests {
3749    use super::*;
3750
3751    #[test]
3752    fn test_parse_circ_path_empty() {
3753        let circuits = parse_circuits("").unwrap();
3754        assert!(circuits.is_empty());
3755    }
3756
3757    #[test]
3758    fn test_parse_circ_path_with_fingerprint_and_nickname() {
3759        let content = "1 BUILT $999A226EBED397F331B612FE1E4CFAE5C1F201BA~piyaz";
3760        let circuits = parse_circuits(content).unwrap();
3761        assert_eq!(circuits.len(), 1);
3762        assert_eq!(circuits[0].path.len(), 1);
3763        assert_eq!(
3764            circuits[0].path[0].fingerprint,
3765            "999A226EBED397F331B612FE1E4CFAE5C1F201BA"
3766        );
3767        assert_eq!(circuits[0].path[0].nickname, Some("piyaz".to_string()));
3768    }
3769
3770    #[test]
3771    fn test_parse_circ_path_multiple_relays() {
3772        let content =
3773            "1 BUILT $E57A476CD4DFBD99B4EE52A100A58610AD6E80B9,$AAAA,$BBBB~PrivacyRepublic14";
3774        let circuits = parse_circuits(content).unwrap();
3775        assert_eq!(circuits.len(), 1);
3776        assert_eq!(circuits[0].path.len(), 3);
3777        assert_eq!(
3778            circuits[0].path[0].fingerprint,
3779            "E57A476CD4DFBD99B4EE52A100A58610AD6E80B9"
3780        );
3781        assert_eq!(circuits[0].path[0].nickname, None);
3782        assert_eq!(circuits[0].path[2].fingerprint, "BBBB");
3783        assert_eq!(
3784            circuits[0].path[2].nickname,
3785            Some("PrivacyRepublic14".to_string())
3786        );
3787    }
3788
3789    #[test]
3790    fn test_get_streams_parsing() {
3791        let content =
3792            "1 NEW 4 10.10.10.1:80\n2 SUCCEEDED 4 10.10.10.1:80\n3 SUCCEEDED 4 10.10.10.1:80";
3793        let streams = parse_streams(content).unwrap();
3794        assert_eq!(streams.len(), 3);
3795
3796        assert_eq!(streams[0].id.0, "1");
3797        assert_eq!(streams[0].status, StreamStatus::New);
3798        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("4")));
3799        assert_eq!(streams[0].target_host, "10.10.10.1");
3800        assert_eq!(streams[0].target_port, 80);
3801
3802        assert_eq!(streams[1].id.0, "2");
3803        assert_eq!(streams[1].status, StreamStatus::Succeeded);
3804
3805        assert_eq!(streams[2].id.0, "3");
3806        assert_eq!(streams[2].status, StreamStatus::Succeeded);
3807    }
3808
3809    #[test]
3810    fn test_circuit_status_parsing() {
3811        let test_cases = [
3812            ("LAUNCHED", CircStatus::Launched),
3813            ("BUILT", CircStatus::Built),
3814            ("GUARD_WAIT", CircStatus::GuardWait),
3815            ("EXTENDED", CircStatus::Extended),
3816            ("FAILED", CircStatus::Failed),
3817            ("CLOSED", CircStatus::Closed),
3818        ];
3819
3820        for (input, expected) in test_cases {
3821            assert_eq!(parse_circ_status(input).unwrap(), expected);
3822        }
3823    }
3824
3825    #[test]
3826    fn test_stream_status_parsing() {
3827        let test_cases = [
3828            ("NEW", StreamStatus::New),
3829            ("NEWRESOLVE", StreamStatus::NewResolve),
3830            ("REMAP", StreamStatus::Remap),
3831            ("SENTCONNECT", StreamStatus::SentConnect),
3832            ("SENTRESOLVE", StreamStatus::SentResolve),
3833            ("SUCCEEDED", StreamStatus::Succeeded),
3834            ("FAILED", StreamStatus::Failed),
3835            ("DETACHED", StreamStatus::Detached),
3836            ("CONTROLLER_WAIT", StreamStatus::ControllerWait),
3837            ("CLOSED", StreamStatus::Closed),
3838        ];
3839
3840        for (input, expected) in test_cases {
3841            assert_eq!(parse_stream_status(input).unwrap(), expected);
3842        }
3843    }
3844
3845    #[test]
3846    fn test_parse_target_various() {
3847        let test_cases = [
3848            ("www.example.com:443", ("www.example.com", 443)),
3849            ("192.168.1.1:80", ("192.168.1.1", 80)),
3850            ("10.10.10.1:8080", ("10.10.10.1", 8080)),
3851            ("[::1]:443", ("[::1]", 443)),
3852        ];
3853
3854        for (input, (expected_host, expected_port)) in test_cases {
3855            let (host, port) = parse_target(input).unwrap();
3856            assert_eq!(host, expected_host);
3857            assert_eq!(port, expected_port);
3858        }
3859    }
3860
3861    #[test]
3862    fn test_parse_circuits_with_build_flags() {
3863        let content = "1 BUILT $AAAA~Guard,$BBBB~Exit BUILD_FLAGS=IS_INTERNAL,NEED_CAPACITY PURPOSE=GENERAL TIME_CREATED=2023-01-01T00:00:00";
3864        let circuits = parse_circuits(content).unwrap();
3865        assert_eq!(circuits.len(), 1);
3866        assert_eq!(circuits[0].status, CircStatus::Built);
3867        assert_eq!(circuits[0].path.len(), 2);
3868    }
3869
3870    #[test]
3871    fn test_parse_circuits_launched_no_path() {
3872        let content = "1 LAUNCHED BUILD_FLAGS=NEED_CAPACITY PURPOSE=GENERAL";
3873        let circuits = parse_circuits(content).unwrap();
3874        assert_eq!(circuits.len(), 1);
3875        assert_eq!(circuits[0].status, CircStatus::Launched);
3876        assert!(circuits[0].path.is_empty());
3877    }
3878
3879    #[test]
3880    fn test_parse_streams_detached() {
3881        let content = "1 DETACHED 0 www.example.com:443";
3882        let streams = parse_streams(content).unwrap();
3883        assert_eq!(streams.len(), 1);
3884        assert_eq!(streams[0].status, StreamStatus::Detached);
3885        assert_eq!(streams[0].circuit_id, None);
3886    }
3887
3888    #[test]
3889    fn test_relay_info_parsing_variations() {
3890        let test_cases = [
3891            ("$ABCD1234~MyRelay", "ABCD1234", Some("MyRelay")),
3892            ("$ABCD1234", "ABCD1234", None),
3893            ("ABCD1234~MyRelay", "ABCD1234", Some("MyRelay")),
3894            ("ABCD1234", "ABCD1234", None),
3895        ];
3896
3897        for (input, expected_fp, expected_nick) in test_cases {
3898            let info = parse_relay_info(input);
3899            assert_eq!(info.fingerprint, expected_fp);
3900            assert_eq!(info.nickname, expected_nick.map(|s| s.to_string()));
3901        }
3902    }
3903
3904    #[test]
3905    fn test_circuit_id_hash() {
3906        use std::collections::HashSet;
3907        let mut set = HashSet::new();
3908        set.insert(CircuitId::new("1"));
3909        set.insert(CircuitId::new("2"));
3910        set.insert(CircuitId::new("1"));
3911        assert_eq!(set.len(), 2);
3912    }
3913
3914    #[test]
3915    fn test_stream_id_hash() {
3916        use std::collections::HashSet;
3917        let mut set = HashSet::new();
3918        set.insert(StreamId::new("1"));
3919        set.insert(StreamId::new("2"));
3920        set.insert(StreamId::new("1"));
3921        assert_eq!(set.len(), 2);
3922    }
3923
3924    #[test]
3925    fn test_parse_circuits_real_world_example() {
3926        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"#;
3927        let circuits = parse_circuits(content).unwrap();
3928        assert_eq!(circuits.len(), 1);
3929        assert_eq!(circuits[0].id.0, "7");
3930        assert_eq!(circuits[0].status, CircStatus::Built);
3931        assert_eq!(circuits[0].path.len(), 3);
3932        assert_eq!(
3933            circuits[0].path[0].nickname,
3934            Some("Quetzalcoatl".to_string())
3935        );
3936        assert_eq!(
3937            circuits[0].path[1].nickname,
3938            Some("DigiGesTor4e3".to_string())
3939        );
3940        assert_eq!(
3941            circuits[0].path[2].nickname,
3942            Some("torserversNet".to_string())
3943        );
3944    }
3945
3946    #[test]
3947    fn test_parse_streams_real_world_example() {
3948        let content =
3949            "42 SUCCEEDED 7 www.torproject.org:443 SOURCE_ADDR=127.0.0.1:12345 PURPOSE=USER";
3950        let streams = parse_streams(content).unwrap();
3951        assert_eq!(streams.len(), 1);
3952        assert_eq!(streams[0].id.0, "42");
3953        assert_eq!(streams[0].status, StreamStatus::Succeeded);
3954        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("7")));
3955        assert_eq!(streams[0].target_host, "www.torproject.org");
3956        assert_eq!(streams[0].target_port, 443);
3957    }
3958
3959    #[test]
3960    fn test_parse_add_onion_response_v3() {
3961        let content = "ServiceID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nPrivateKey=ED25519-V3:base64keydata==";
3962        let response = parse_add_onion_response(content).unwrap();
3963        assert_eq!(
3964            response.service_id,
3965            "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
3966        );
3967        assert_eq!(response.private_key_type, Some("ED25519-V3".to_string()));
3968        assert_eq!(response.private_key, Some("base64keydata==".to_string()));
3969    }
3970
3971    #[test]
3972    fn test_parse_add_onion_response_discarded_key() {
3973        let content = "ServiceID=abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv";
3974        let response = parse_add_onion_response(content).unwrap();
3975        assert_eq!(
3976            response.service_id,
3977            "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv"
3978        );
3979        assert!(response.private_key.is_none());
3980        assert!(response.private_key_type.is_none());
3981    }
3982
3983    #[test]
3984    fn test_parse_add_onion_response_missing_service_id() {
3985        let content = "PrivateKey=ED25519-V3:base64keydata==";
3986        let result = parse_add_onion_response(content);
3987        assert!(result.is_err());
3988    }
3989
3990    #[test]
3991    fn test_listener_type_display() {
3992        assert_eq!(ListenerType::Or.to_string(), "or");
3993        assert_eq!(ListenerType::Dir.to_string(), "dir");
3994        assert_eq!(ListenerType::Socks.to_string(), "socks");
3995        assert_eq!(ListenerType::Trans.to_string(), "trans");
3996        assert_eq!(ListenerType::Natd.to_string(), "natd");
3997        assert_eq!(ListenerType::Dns.to_string(), "dns");
3998        assert_eq!(ListenerType::Control.to_string(), "control");
3999        assert_eq!(ListenerType::ExtOr.to_string(), "extor");
4000        assert_eq!(ListenerType::HttpTunnel.to_string(), "httptunnel");
4001    }
4002
4003    #[test]
4004    fn test_circuit_purpose_display() {
4005        assert_eq!(CircuitPurpose::General.to_string(), "general");
4006        assert_eq!(CircuitPurpose::Controller.to_string(), "controller");
4007    }
4008
4009    #[test]
4010    fn test_parse_listeners_single() {
4011        let response = r#""127.0.0.1:9050""#;
4012        let listeners = parse_listeners(response).unwrap();
4013        assert_eq!(listeners.len(), 1);
4014        assert_eq!(listeners[0], ("127.0.0.1".to_string(), 9050));
4015    }
4016
4017    #[test]
4018    fn test_parse_listeners_multiple() {
4019        let response = r#""127.0.0.1:9050" "0.0.0.0:9051""#;
4020        let listeners = parse_listeners(response).unwrap();
4021        assert_eq!(listeners.len(), 2);
4022        assert_eq!(listeners[0], ("127.0.0.1".to_string(), 9050));
4023        assert_eq!(listeners[1], ("0.0.0.0".to_string(), 9051));
4024    }
4025
4026    #[test]
4027    fn test_parse_listeners_ipv6() {
4028        let response = r#""[::1]:9050""#;
4029        let listeners = parse_listeners(response).unwrap();
4030        assert_eq!(listeners.len(), 1);
4031        assert_eq!(listeners[0], ("::1".to_string(), 9050));
4032    }
4033
4034    #[test]
4035    fn test_parse_listeners_empty() {
4036        let response = "";
4037        let listeners = parse_listeners(response).unwrap();
4038        assert!(listeners.is_empty());
4039    }
4040
4041    #[test]
4042    fn test_parse_listeners_unix_socket_skipped() {
4043        let response = r#""unix:/tmp/tor/socket" "127.0.0.1:9050""#;
4044        let listeners = parse_listeners(response).unwrap();
4045        assert_eq!(listeners.len(), 1);
4046        assert_eq!(listeners[0], ("127.0.0.1".to_string(), 9050));
4047    }
4048
4049    #[test]
4050    fn test_parse_protocolinfo_basic() {
4051        let content = r#"PROTOCOLINFO 1
4052AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE="/var/run/tor/control.authcookie"
4053VERSION Tor="0.4.7.10"
4054OK"#;
4055        let info = parse_protocolinfo(content).unwrap();
4056        assert_eq!(info.protocol_version, 1);
4057        assert_eq!(info.tor_version, "0.4.7.10");
4058        assert_eq!(info.auth_methods, vec!["COOKIE", "SAFECOOKIE"]);
4059        assert_eq!(
4060            info.cookie_file,
4061            Some("/var/run/tor/control.authcookie".to_string())
4062        );
4063    }
4064
4065    #[test]
4066    fn test_parse_protocolinfo_null_auth() {
4067        let content = r#"PROTOCOLINFO 1
4068AUTH METHODS=NULL
4069VERSION Tor="0.4.7.10"
4070OK"#;
4071        let info = parse_protocolinfo(content).unwrap();
4072        assert_eq!(info.auth_methods, vec!["NULL"]);
4073        assert!(info.cookie_file.is_none());
4074    }
4075
4076    #[test]
4077    fn test_parse_accounting_bytes() {
4078        let bytes_str = "1234567 7654321";
4079        let (read, written) = parse_accounting_bytes(bytes_str).unwrap();
4080        assert_eq!(read, 1234567);
4081        assert_eq!(written, 7654321);
4082    }
4083
4084    #[test]
4085    fn test_parse_accounting_bytes_zero() {
4086        let bytes_str = "0 0";
4087        let (read, written) = parse_accounting_bytes(bytes_str).unwrap();
4088        assert_eq!(read, 0);
4089        assert_eq!(written, 0);
4090    }
4091
4092    #[test]
4093    fn test_parse_accounting_bytes_invalid() {
4094        let bytes_str = "invalid";
4095        assert!(parse_accounting_bytes(bytes_str).is_err());
4096    }
4097
4098    #[test]
4099    fn test_listener_type_equality() {
4100        assert_eq!(ListenerType::Socks, ListenerType::Socks);
4101        assert_ne!(ListenerType::Socks, ListenerType::Control);
4102    }
4103
4104    #[test]
4105    fn test_circuit_purpose_equality() {
4106        assert_eq!(CircuitPurpose::General, CircuitPurpose::General);
4107        assert_ne!(CircuitPurpose::General, CircuitPurpose::Controller);
4108    }
4109
4110    #[test]
4111    fn test_listener_type_hash() {
4112        let mut set = HashSet::new();
4113        set.insert(ListenerType::Socks);
4114        set.insert(ListenerType::Control);
4115        set.insert(ListenerType::Socks);
4116        assert_eq!(set.len(), 2);
4117    }
4118}