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}
683
684impl Controller {
685    /// Creates a new Controller connected to a TCP control port.
686    ///
687    /// Establishes a TCP connection to Tor's control port at the specified
688    /// address. The connection is unauthenticated; call [`authenticate`](Self::authenticate)
689    /// before issuing commands.
690    ///
691    /// # Arguments
692    ///
693    /// * `addr` - The socket address of Tor's control port (e.g., `127.0.0.1:9051`)
694    ///
695    /// # Errors
696    ///
697    /// Returns [`Error::Socket`] if:
698    /// - The connection is refused (Tor not running or port incorrect)
699    /// - Network is unreachable
700    /// - Connection times out
701    ///
702    /// # Example
703    ///
704    /// ```rust,no_run
705    /// use stem_rs::controller::Controller;
706    ///
707    /// # async fn example() -> Result<(), stem_rs::Error> {
708    /// // Connect to default control port
709    /// let controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
710    ///
711    /// // Connect to custom port
712    /// let controller = Controller::from_port("127.0.0.1:9151".parse()?).await?;
713    /// # Ok(())
714    /// # }
715    /// ```
716    ///
717    /// # See Also
718    ///
719    /// - [`from_socket_file`](Self::from_socket_file): Connect via Unix socket
720    /// - [`authenticate`](Self::authenticate): Authenticate after connecting
721    pub async fn from_port(addr: SocketAddr) -> Result<Self, Error> {
722        let socket = ControlSocket::connect_port(addr).await?;
723        Ok(Self {
724            socket,
725            event_buffer: Vec::new(),
726        })
727    }
728
729    /// Creates a new Controller connected to a Unix domain socket.
730    ///
731    /// Establishes a connection to Tor's control socket at the specified
732    /// file path. This is typically more secure than TCP as it doesn't
733    /// expose the control interface to the network.
734    ///
735    /// # Arguments
736    ///
737    /// * `path` - Path to Tor's control socket file (e.g., `/var/run/tor/control`)
738    ///
739    /// # Errors
740    ///
741    /// Returns [`Error::Socket`] if:
742    /// - The socket file doesn't exist
743    /// - Permission denied accessing the socket
744    /// - The socket is not a valid Unix domain socket
745    ///
746    /// # Example
747    ///
748    /// ```rust,no_run
749    /// use std::path::Path;
750    /// use stem_rs::controller::Controller;
751    ///
752    /// # async fn example() -> Result<(), stem_rs::Error> {
753    /// // Connect to Tor's Unix control socket
754    /// let controller = Controller::from_socket_file(
755    ///     Path::new("/var/run/tor/control")
756    /// ).await?;
757    /// # Ok(())
758    /// # }
759    /// ```
760    ///
761    /// # Platform Support
762    ///
763    /// Unix domain sockets are only available on Unix-like systems (Linux, macOS, BSD).
764    /// On Windows, use [`from_port`](Self::from_port) instead.
765    ///
766    /// # See Also
767    ///
768    /// - [`from_port`](Self::from_port): Connect via TCP
769    /// - [`authenticate`](Self::authenticate): Authenticate after connecting
770    pub async fn from_socket_file(path: &Path) -> Result<Self, Error> {
771        let socket = ControlSocket::connect_unix(path).await?;
772        Ok(Self {
773            socket,
774            event_buffer: Vec::new(),
775        })
776    }
777
778    /// Receives a response, buffering any asynchronous events.
779    ///
780    /// This internal method reads responses from the socket, automatically
781    /// buffering any asynchronous events (status code 650) that arrive
782    /// while waiting for a command response.
783    async fn recv_response(&mut self) -> Result<ControlMessage, Error> {
784        loop {
785            let response = self.socket.recv().await?;
786            if response.status_code == 650 {
787                self.event_buffer.push(response);
788            } else {
789                return Ok(response);
790            }
791        }
792    }
793
794    /// Authenticates with the Tor control interface.
795    ///
796    /// Attempts authentication using the best available method. If `password`
797    /// is provided, PASSWORD authentication is attempted. Otherwise, the method
798    /// is auto-detected from PROTOCOLINFO.
799    ///
800    /// # Authentication Methods
801    ///
802    /// Methods are tried in this order:
803    /// 1. **NONE** - If control port is open (no auth required)
804    /// 2. **SAFECOOKIE** - Preferred for local connections
805    /// 3. **COOKIE** - Fallback for older Tor versions
806    /// 4. **PASSWORD** - If password is provided
807    ///
808    /// # Arguments
809    ///
810    /// * `password` - Optional password for PASSWORD authentication
811    ///
812    /// # Preconditions
813    ///
814    /// - Socket must be connected (not closed)
815    /// - No prior successful authentication on this connection
816    ///
817    /// # Postconditions
818    ///
819    /// - On success: Controller is authenticated and ready for commands
820    /// - On failure: Connection state is undefined; reconnect recommended
821    ///
822    /// # Errors
823    ///
824    /// Returns [`Error::Authentication`] with specific reason:
825    ///
826    /// - [`AuthError::NoMethods`](crate::AuthError::NoMethods) - No compatible auth methods available
827    /// - [`AuthError::IncorrectPassword`](crate::AuthError::IncorrectPassword) - PASSWORD auth failed
828    /// - [`AuthError::CookieUnreadable`](crate::AuthError::CookieUnreadable) - Cannot read cookie file
829    /// - [`AuthError::IncorrectCookie`](crate::AuthError::IncorrectCookie) - COOKIE auth failed
830    /// - [`AuthError::ChallengeFailed`](crate::AuthError::ChallengeFailed) - SAFECOOKIE challenge failed
831    ///
832    /// # Example
833    ///
834    /// ```rust,no_run
835    /// use stem_rs::controller::Controller;
836    ///
837    /// # async fn example() -> Result<(), stem_rs::Error> {
838    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
839    ///
840    /// // Auto-detect authentication method
841    /// controller.authenticate(None).await?;
842    ///
843    /// // Or use password authentication
844    /// controller.authenticate(Some("my_password")).await?;
845    /// # Ok(())
846    /// # }
847    /// ```
848    ///
849    /// # Security
850    ///
851    /// - Passwords are cleared from memory after use
852    /// - Cookie comparison uses constant-time algorithm
853    /// - SAFECOOKIE nonces are cryptographically random
854    ///
855    /// # See Also
856    ///
857    /// - [`auth`]: Authentication implementation details
858    /// - [`AuthError`](crate::AuthError): Authentication error types
859    pub async fn authenticate(&mut self, password: Option<&str>) -> Result<(), Error> {
860        auth::authenticate(&mut self.socket, password).await
861    }
862
863    /// Queries Tor for information using the GETINFO command.
864    ///
865    /// GETINFO retrieves various pieces of information from Tor. The available
866    /// keys depend on Tor's version and configuration.
867    ///
868    /// # Arguments
869    ///
870    /// * `key` - The information key to query (e.g., "version", "circuit-status")
871    ///
872    /// # Common Keys
873    ///
874    /// | Key | Description |
875    /// |-----|-------------|
876    /// | `version` | Tor version string |
877    /// | `process/pid` | Tor process ID |
878    /// | `circuit-status` | Active circuit information |
879    /// | `stream-status` | Active stream information |
880    /// | `address` | Best guess at external IP address |
881    /// | `fingerprint` | Relay fingerprint (if running as relay) |
882    /// | `config-file` | Path to torrc file |
883    ///
884    /// # Errors
885    ///
886    /// Returns [`Error::OperationFailed`] if:
887    /// - The key is unrecognized
888    /// - The information is not available
889    /// - Tor returns an error response
890    ///
891    /// # Example
892    ///
893    /// ```rust,no_run
894    /// use stem_rs::controller::Controller;
895    ///
896    /// # async fn example() -> Result<(), stem_rs::Error> {
897    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
898    /// controller.authenticate(None).await?;
899    ///
900    /// // Query Tor version
901    /// let version = controller.get_info("version").await?;
902    /// println!("Tor version: {}", version);
903    ///
904    /// // Query external IP address
905    /// let address = controller.get_info("address").await?;
906    /// println!("External IP: {}", address);
907    /// # Ok(())
908    /// # }
909    /// ```
910    ///
911    /// # See Also
912    ///
913    /// - [`get_version`](Self::get_version): Typed version query
914    /// - [`get_pid`](Self::get_pid): Typed PID query
915    pub async fn get_info(&mut self, key: &str) -> Result<String, Error> {
916        let command = format!("GETINFO {}", key);
917        self.socket.send(&command).await?;
918        let response = self.recv_response().await?;
919
920        if !response.is_ok() {
921            return Err(Error::OperationFailed {
922                code: response.status_code.to_string(),
923                message: response.content().to_string(),
924            });
925        }
926
927        for line in &response.lines {
928            if let Some(rest) = line.strip_prefix(&format!("{}=", key)) {
929                return Ok(rest.to_string());
930            }
931            if line.starts_with(&format!("{}\n", key)) {
932                return Ok(line
933                    .strip_prefix(&format!("{}\n", key))
934                    .unwrap_or("")
935                    .to_string());
936            }
937        }
938
939        Ok(response.content().to_string())
940    }
941
942    /// Retrieves the Tor version as a parsed [`Version`] object.
943    ///
944    /// This is a convenience wrapper around [`get_info("version")`](Self::get_info)
945    /// that parses the version string into a structured [`Version`] type.
946    ///
947    /// # Errors
948    ///
949    /// Returns an error if:
950    /// - The GETINFO command fails
951    /// - The version string cannot be parsed
952    ///
953    /// # Example
954    ///
955    /// ```rust,no_run
956    /// use stem_rs::controller::Controller;
957    ///
958    /// # async fn example() -> Result<(), stem_rs::Error> {
959    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
960    /// controller.authenticate(None).await?;
961    ///
962    /// let version = controller.get_version().await?;
963    /// println!("Tor version: {}", version);
964    ///
965    /// // Version supports comparison
966    /// // if version >= Version::parse("0.4.0.0")? { ... }
967    /// # Ok(())
968    /// # }
969    /// ```
970    ///
971    /// # See Also
972    ///
973    /// - [`Version`]: Version type with comparison support
974    pub async fn get_version(&mut self) -> Result<Version, Error> {
975        let version_str = self.get_info("version").await?;
976        Version::parse(&version_str)
977    }
978
979    /// Retrieves the process ID of the Tor process.
980    ///
981    /// This is a convenience wrapper around [`get_info("process/pid")`](Self::get_info)
982    /// that parses the PID into a `u32`.
983    ///
984    /// # Errors
985    ///
986    /// Returns an error if:
987    /// - The GETINFO command fails
988    /// - The PID string cannot be parsed as a number
989    ///
990    /// # Example
991    ///
992    /// ```rust,no_run
993    /// use stem_rs::controller::Controller;
994    ///
995    /// # async fn example() -> Result<(), stem_rs::Error> {
996    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
997    /// controller.authenticate(None).await?;
998    ///
999    /// let pid = controller.get_pid().await?;
1000    /// println!("Tor PID: {}", pid);
1001    /// # Ok(())
1002    /// # }
1003    /// ```
1004    pub async fn get_pid(&mut self) -> Result<u32, Error> {
1005        let pid_str = self.get_info("process/pid").await?;
1006        pid_str.parse().map_err(|_| Error::Parse {
1007            location: "pid".to_string(),
1008            reason: format!("invalid pid: {}", pid_str),
1009        })
1010    }
1011
1012    /// Retrieves the value(s) of a Tor configuration option.
1013    ///
1014    /// Uses the GETCONF command to query Tor's current configuration.
1015    /// Some options can have multiple values, so this returns a `Vec<String>`.
1016    ///
1017    /// # Arguments
1018    ///
1019    /// * `key` - The configuration option name (e.g., "SocksPort", "ExitPolicy")
1020    ///
1021    /// # Errors
1022    ///
1023    /// Returns [`Error::OperationFailed`] if:
1024    /// - The configuration option is unrecognized
1025    /// - Tor returns an error response
1026    ///
1027    /// # Example
1028    ///
1029    /// ```rust,no_run
1030    /// use stem_rs::controller::Controller;
1031    ///
1032    /// # async fn example() -> Result<(), stem_rs::Error> {
1033    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1034    /// controller.authenticate(None).await?;
1035    ///
1036    /// // Get SOCKS port configuration
1037    /// let socks_ports = controller.get_conf("SocksPort").await?;
1038    /// for port in socks_ports {
1039    ///     println!("SOCKS port: {}", port);
1040    /// }
1041    /// # Ok(())
1042    /// # }
1043    /// ```
1044    ///
1045    /// # See Also
1046    ///
1047    /// - [`set_conf`](Self::set_conf): Set a configuration option
1048    /// - [`reset_conf`](Self::reset_conf): Reset to default value
1049    pub async fn get_conf(&mut self, key: &str) -> Result<Vec<String>, Error> {
1050        let command = format!("GETCONF {}", key);
1051        self.socket.send(&command).await?;
1052        let response = self.recv_response().await?;
1053
1054        if !response.is_ok() {
1055            return Err(Error::OperationFailed {
1056                code: response.status_code.to_string(),
1057                message: response.content().to_string(),
1058            });
1059        }
1060
1061        let mut values = Vec::new();
1062        for line in &response.lines {
1063            if let Some(rest) = line.strip_prefix(&format!("{}=", key)) {
1064                values.push(rest.to_string());
1065            } else if line
1066                .to_lowercase()
1067                .starts_with(&format!("{}=", key.to_lowercase()))
1068            {
1069                let eq_pos = line.find('=').unwrap_or(line.len());
1070                values.push(line[eq_pos + 1..].to_string());
1071            }
1072        }
1073
1074        if values.is_empty() && !response.lines.is_empty() {
1075            let first_line = &response.lines[0];
1076            if let Some(eq_pos) = first_line.find('=') {
1077                values.push(first_line[eq_pos + 1..].to_string());
1078            }
1079        }
1080
1081        Ok(values)
1082    }
1083
1084    /// Sets a Tor configuration option.
1085    ///
1086    /// Uses the SETCONF command to change Tor's configuration at runtime.
1087    /// The change takes effect immediately but is not persisted to the torrc
1088    /// file unless you call `save_conf`.
1089    ///
1090    /// # Arguments
1091    ///
1092    /// * `key` - The configuration option name
1093    /// * `value` - The new value for the option
1094    ///
1095    /// # Value Escaping
1096    ///
1097    /// Values containing spaces or quotes are automatically escaped. You don't
1098    /// need to handle quoting yourself.
1099    ///
1100    /// # Errors
1101    ///
1102    /// Returns [`Error::OperationFailed`] if:
1103    /// - The configuration option is unrecognized
1104    /// - The value is invalid for this option
1105    /// - The option cannot be changed at runtime
1106    /// - Tor returns an error response
1107    ///
1108    /// # Example
1109    ///
1110    /// ```rust,no_run
1111    /// use stem_rs::controller::Controller;
1112    ///
1113    /// # async fn example() -> Result<(), stem_rs::Error> {
1114    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1115    /// controller.authenticate(None).await?;
1116    ///
1117    /// // Change bandwidth rate
1118    /// controller.set_conf("BandwidthRate", "1 MB").await?;
1119    ///
1120    /// // Enable a feature
1121    /// controller.set_conf("SafeLogging", "1").await?;
1122    /// # Ok(())
1123    /// # }
1124    /// ```
1125    ///
1126    /// # See Also
1127    ///
1128    /// - [`get_conf`](Self::get_conf): Get current configuration
1129    /// - [`reset_conf`](Self::reset_conf): Reset to default value
1130    pub async fn set_conf(&mut self, key: &str, value: &str) -> Result<(), Error> {
1131        let command = if value.contains(' ') || value.contains('"') {
1132            format!(
1133                "SETCONF {}=\"{}\"",
1134                key,
1135                value.replace('\\', "\\\\").replace('"', "\\\"")
1136            )
1137        } else {
1138            format!("SETCONF {}={}", key, value)
1139        };
1140        self.socket.send(&command).await?;
1141        let response = self.recv_response().await?;
1142
1143        if response.is_ok() {
1144            Ok(())
1145        } else {
1146            Err(Error::OperationFailed {
1147                code: response.status_code.to_string(),
1148                message: response.content().to_string(),
1149            })
1150        }
1151    }
1152
1153    /// Resets a Tor configuration option to its default value.
1154    ///
1155    /// Uses the RESETCONF command to restore a configuration option to its
1156    /// default value as if it were not set in the torrc file.
1157    ///
1158    /// # Arguments
1159    ///
1160    /// * `key` - The configuration option name to reset
1161    ///
1162    /// # Errors
1163    ///
1164    /// Returns [`Error::OperationFailed`] if:
1165    /// - The configuration option is unrecognized
1166    /// - The option cannot be reset at runtime
1167    /// - Tor returns an error response
1168    ///
1169    /// # Example
1170    ///
1171    /// ```rust,no_run
1172    /// use stem_rs::controller::Controller;
1173    ///
1174    /// # async fn example() -> Result<(), stem_rs::Error> {
1175    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1176    /// controller.authenticate(None).await?;
1177    ///
1178    /// // Reset bandwidth rate to default
1179    /// controller.reset_conf("BandwidthRate").await?;
1180    /// # Ok(())
1181    /// # }
1182    /// ```
1183    ///
1184    /// # See Also
1185    ///
1186    /// - [`get_conf`](Self::get_conf): Get current configuration
1187    /// - [`set_conf`](Self::set_conf): Set a configuration option
1188    pub async fn reset_conf(&mut self, key: &str) -> Result<(), Error> {
1189        let command = format!("RESETCONF {}", key);
1190        self.socket.send(&command).await?;
1191        let response = self.recv_response().await?;
1192
1193        if response.is_ok() {
1194            Ok(())
1195        } else {
1196            Err(Error::OperationFailed {
1197                code: response.status_code.to_string(),
1198                message: response.content().to_string(),
1199            })
1200        }
1201    }
1202
1203    /// Saves the current configuration to the torrc file.
1204    ///
1205    /// This persists any configuration changes made via [`set_conf`](Self::set_conf)
1206    /// to Tor's configuration file, so they survive restarts.
1207    ///
1208    /// # Arguments
1209    ///
1210    /// * `force` - If `true`, overwrite the configuration even if it includes
1211    ///   a `%include` clause. This is ignored if Tor doesn't support it.
1212    ///
1213    /// # Errors
1214    ///
1215    /// Returns [`Error::OperationFailed`] if:
1216    /// - Tor is unable to save the configuration file (e.g., permission denied)
1217    /// - The configuration file contains `%include` and `force` is `false`
1218    ///
1219    /// # Example
1220    ///
1221    /// ```rust,no_run
1222    /// use stem_rs::controller::Controller;
1223    ///
1224    /// # async fn example() -> Result<(), stem_rs::Error> {
1225    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1226    /// controller.authenticate(None).await?;
1227    ///
1228    /// // Change a configuration option
1229    /// controller.set_conf("BandwidthRate", "1 MB").await?;
1230    ///
1231    /// // Save to torrc
1232    /// controller.save_conf(false).await?;
1233    ///
1234    /// // Force save even with %include
1235    /// controller.save_conf(true).await?;
1236    /// # Ok(())
1237    /// # }
1238    /// ```
1239    ///
1240    /// # See Also
1241    ///
1242    /// - [`set_conf`](Self::set_conf): Set a configuration option
1243    /// - [`get_conf`](Self::get_conf): Get current configuration
1244    pub async fn save_conf(&mut self, force: bool) -> Result<(), Error> {
1245        let command = if force { "SAVECONF FORCE" } else { "SAVECONF" };
1246        self.socket.send(command).await?;
1247        let response = self.recv_response().await?;
1248
1249        if response.is_ok() {
1250            Ok(())
1251        } else if response.status_code == 551 {
1252            Err(Error::OperationFailed {
1253                code: response.status_code.to_string(),
1254                message: response.content().to_string(),
1255            })
1256        } else {
1257            Err(Error::Protocol(format!(
1258                "SAVECONF returned unexpected response code: {}",
1259                response.status_code
1260            )))
1261        }
1262    }
1263
1264    /// Sends a signal to the Tor process.
1265    ///
1266    /// Signals control various aspects of Tor's behavior, from requesting
1267    /// new circuits to initiating shutdown.
1268    ///
1269    /// # Arguments
1270    ///
1271    /// * `signal` - The signal to send (see [`Signal`])
1272    ///
1273    /// # Available Signals
1274    ///
1275    /// | Signal | Description |
1276    /// |--------|-------------|
1277    /// | [`Reload`](crate::Signal::Reload) | Reload configuration (SIGHUP) |
1278    /// | [`Shutdown`](crate::Signal::Shutdown) | Controlled shutdown |
1279    /// | [`Dump`](crate::Signal::Dump) | Write statistics to disk |
1280    /// | [`Debug`](crate::Signal::Debug) | Switch to debug logging |
1281    /// | [`Halt`](crate::Signal::Halt) | Immediate shutdown (SIGTERM) |
1282    /// | [`Newnym`](crate::Signal::Newnym) | Request new circuits |
1283    /// | [`ClearDnsCache`](crate::Signal::ClearDnsCache) | Clear DNS cache |
1284    /// | [`Heartbeat`](crate::Signal::Heartbeat) | Trigger heartbeat log |
1285    /// | [`Active`](crate::Signal::Active) | Wake from dormant mode |
1286    /// | [`Dormant`](crate::Signal::Dormant) | Enter dormant mode |
1287    ///
1288    /// # Errors
1289    ///
1290    /// Returns [`Error::OperationFailed`] if:
1291    /// - The signal is not recognized
1292    /// - The signal cannot be sent (e.g., rate-limited NEWNYM)
1293    /// - Tor returns an error response
1294    ///
1295    /// # Example
1296    ///
1297    /// ```rust,no_run
1298    /// use stem_rs::controller::Controller;
1299    /// use stem_rs::Signal;
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    /// // Request new identity (new circuits)
1306    /// controller.signal(Signal::Newnym).await?;
1307    ///
1308    /// // Reload configuration
1309    /// controller.signal(Signal::Reload).await?;
1310    ///
1311    /// // Clear DNS cache
1312    /// controller.signal(Signal::ClearDnsCache).await?;
1313    /// # Ok(())
1314    /// # }
1315    /// ```
1316    ///
1317    /// # Rate Limiting
1318    ///
1319    /// The `Newnym` signal is rate-limited by Tor to prevent abuse. If called
1320    /// too frequently, Tor may delay the signal or return an error.
1321    ///
1322    /// # See Also
1323    ///
1324    /// - [`Signal`]: Signal enumeration
1325    pub async fn signal(&mut self, signal: Signal) -> Result<(), Error> {
1326        let command = format!("SIGNAL {}", signal);
1327        self.socket.send(&command).await?;
1328        let response = self.recv_response().await?;
1329
1330        if response.is_ok() {
1331            Ok(())
1332        } else {
1333            Err(Error::OperationFailed {
1334                code: response.status_code.to_string(),
1335                message: response.content().to_string(),
1336            })
1337        }
1338    }
1339
1340    /// Retrieves information about all active circuits.
1341    ///
1342    /// Returns a list of all circuits currently known to Tor, including
1343    /// their status and path information.
1344    ///
1345    /// # Errors
1346    ///
1347    /// Returns an error if:
1348    /// - The GETINFO command fails
1349    /// - The circuit status cannot be parsed
1350    ///
1351    /// # Example
1352    ///
1353    /// ```rust,no_run
1354    /// use stem_rs::controller::Controller;
1355    /// use stem_rs::CircStatus;
1356    ///
1357    /// # async fn example() -> Result<(), stem_rs::Error> {
1358    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1359    /// controller.authenticate(None).await?;
1360    ///
1361    /// let circuits = controller.get_circuits().await?;
1362    /// for circuit in circuits {
1363    ///     if circuit.status == CircStatus::Built {
1364    ///         println!("Circuit {} is ready with {} hops",
1365    ///             circuit.id, circuit.path.len());
1366    ///     }
1367    /// }
1368    /// # Ok(())
1369    /// # }
1370    /// ```
1371    ///
1372    /// # See Also
1373    ///
1374    /// - [`Circuit`]: Circuit information structure
1375    /// - [`new_circuit`](Self::new_circuit): Create a new circuit
1376    /// - [`close_circuit`](Self::close_circuit): Close a circuit
1377    pub async fn get_circuits(&mut self) -> Result<Vec<Circuit>, Error> {
1378        let response_str = self.get_info("circuit-status").await?;
1379        parse_circuits(&response_str)
1380    }
1381
1382    /// Creates a new circuit, optionally with a specified path.
1383    ///
1384    /// If no path is specified, Tor will select relays automatically based
1385    /// on its path selection algorithm. If a path is provided, Tor will
1386    /// attempt to build a circuit through those specific relays.
1387    ///
1388    /// # Arguments
1389    ///
1390    /// * `path` - Optional list of relay fingerprints or nicknames for the circuit path
1391    ///
1392    /// # Path Specification
1393    ///
1394    /// Relays can be specified by:
1395    /// - Fingerprint: `$9695DFC35FFEB861329B9F1AB04C46397020CE31`
1396    /// - Nickname: `MyRelay`
1397    /// - Fingerprint with nickname: `$9695DFC35FFEB861329B9F1AB04C46397020CE31~MyRelay`
1398    ///
1399    /// # Errors
1400    ///
1401    /// Returns [`Error::OperationFailed`] if:
1402    /// - A specified relay is unknown or unavailable
1403    /// - The path is invalid (e.g., too short)
1404    /// - Circuit creation fails
1405    ///
1406    /// # Example
1407    ///
1408    /// ```rust,no_run
1409    /// use stem_rs::controller::Controller;
1410    ///
1411    /// # async fn example() -> Result<(), stem_rs::Error> {
1412    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1413    /// controller.authenticate(None).await?;
1414    ///
1415    /// // Create circuit with automatic path selection
1416    /// let circuit_id = controller.new_circuit(None).await?;
1417    /// println!("Created circuit: {}", circuit_id);
1418    ///
1419    /// // Create circuit with specific path
1420    /// let path = &["$AAAA...", "$BBBB...", "$CCCC..."];
1421    /// let circuit_id = controller.new_circuit(Some(path)).await?;
1422    /// # Ok(())
1423    /// # }
1424    /// ```
1425    ///
1426    /// # See Also
1427    ///
1428    /// - [`extend_circuit`](Self::extend_circuit): Extend an existing circuit
1429    /// - [`close_circuit`](Self::close_circuit): Close a circuit
1430    /// - [`get_circuits`](Self::get_circuits): List active circuits
1431    pub async fn new_circuit(&mut self, path: Option<&[&str]>) -> Result<CircuitId, Error> {
1432        let command = match path {
1433            Some(relays) if !relays.is_empty() => {
1434                format!("EXTENDCIRCUIT 0 {}", relays.join(","))
1435            }
1436            _ => "EXTENDCIRCUIT 0".to_string(),
1437        };
1438        self.socket.send(&command).await?;
1439        let response = self.recv_response().await?;
1440
1441        if !response.is_ok() {
1442            return Err(Error::OperationFailed {
1443                code: response.status_code.to_string(),
1444                message: response.content().to_string(),
1445            });
1446        }
1447
1448        let content = response.content();
1449        let mut line = ControlLine::new(content);
1450        if line.is_next_mapping(Some("EXTENDED"), false) {
1451            let (_, circuit_id) = line.pop_mapping(false, false)?;
1452            return Ok(CircuitId::new(circuit_id));
1453        }
1454
1455        let circuit_id = line.pop(false, false)?;
1456        Ok(CircuitId::new(circuit_id))
1457    }
1458
1459    /// Extends an existing circuit by adding additional hops.
1460    ///
1461    /// Adds one or more relays to an existing circuit. The circuit must be
1462    /// in a state that allows extension (typically BUILT or EXTENDED).
1463    ///
1464    /// # Arguments
1465    ///
1466    /// * `id` - The circuit ID to extend
1467    /// * `path` - List of relay fingerprints or nicknames to add
1468    ///
1469    /// # Errors
1470    ///
1471    /// Returns [`Error::InvalidArguments`] if:
1472    /// - The path is empty
1473    ///
1474    /// Returns [`Error::CircuitExtensionFailed`] if:
1475    /// - The circuit doesn't exist
1476    /// - The circuit is in a state that doesn't allow extension
1477    /// - A specified relay is unknown or unavailable
1478    /// - The extension fails for any other reason
1479    ///
1480    /// # Example
1481    ///
1482    /// ```rust,no_run
1483    /// use stem_rs::controller::Controller;
1484    ///
1485    /// # async fn example() -> Result<(), stem_rs::Error> {
1486    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1487    /// controller.authenticate(None).await?;
1488    ///
1489    /// // Create a circuit and extend it
1490    /// let circuit_id = controller.new_circuit(None).await?;
1491    /// controller.extend_circuit(&circuit_id, &["$DDDD..."]).await?;
1492    /// # Ok(())
1493    /// # }
1494    /// ```
1495    ///
1496    /// # See Also
1497    ///
1498    /// - [`new_circuit`](Self::new_circuit): Create a new circuit
1499    /// - [`close_circuit`](Self::close_circuit): Close a circuit
1500    pub async fn extend_circuit(&mut self, id: &CircuitId, path: &[&str]) -> Result<(), Error> {
1501        if path.is_empty() {
1502            return Err(Error::InvalidArguments("path cannot be empty".to_string()));
1503        }
1504        let command = format!("EXTENDCIRCUIT {} {}", id.0, path.join(","));
1505        self.socket.send(&command).await?;
1506        let response = self.recv_response().await?;
1507
1508        if response.is_ok() {
1509            Ok(())
1510        } else {
1511            Err(Error::CircuitExtensionFailed(
1512                response.content().to_string(),
1513            ))
1514        }
1515    }
1516
1517    /// Closes an existing circuit.
1518    ///
1519    /// Tears down the specified circuit, closing all streams attached to it.
1520    ///
1521    /// # Arguments
1522    ///
1523    /// * `id` - The circuit ID to close
1524    ///
1525    /// # Errors
1526    ///
1527    /// Returns [`Error::OperationFailed`] if:
1528    /// - The circuit doesn't exist
1529    /// - The circuit is already closed
1530    /// - Tor returns an error response
1531    ///
1532    /// # Example
1533    ///
1534    /// ```rust,no_run
1535    /// use stem_rs::controller::Controller;
1536    ///
1537    /// # async fn example() -> Result<(), stem_rs::Error> {
1538    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1539    /// controller.authenticate(None).await?;
1540    ///
1541    /// // Create and then close a circuit
1542    /// let circuit_id = controller.new_circuit(None).await?;
1543    /// controller.close_circuit(&circuit_id).await?;
1544    /// # Ok(())
1545    /// # }
1546    /// ```
1547    ///
1548    /// # See Also
1549    ///
1550    /// - [`new_circuit`](Self::new_circuit): Create a new circuit
1551    /// - [`get_circuits`](Self::get_circuits): List active circuits
1552    pub async fn close_circuit(&mut self, id: &CircuitId) -> Result<(), Error> {
1553        let command = format!("CLOSECIRCUIT {}", id.0);
1554        self.socket.send(&command).await?;
1555        let response = self.recv_response().await?;
1556
1557        if response.is_ok() {
1558            Ok(())
1559        } else {
1560            Err(Error::OperationFailed {
1561                code: response.status_code.to_string(),
1562                message: response.content().to_string(),
1563            })
1564        }
1565    }
1566
1567    /// Retrieves information about all active streams.
1568    ///
1569    /// Returns a list of all streams currently known to Tor, including
1570    /// their status, target, and circuit attachment.
1571    ///
1572    /// # Errors
1573    ///
1574    /// Returns an error if:
1575    /// - The GETINFO command fails
1576    /// - The stream status cannot be parsed
1577    ///
1578    /// # Example
1579    ///
1580    /// ```rust,no_run
1581    /// use stem_rs::controller::Controller;
1582    /// use stem_rs::StreamStatus;
1583    ///
1584    /// # async fn example() -> Result<(), stem_rs::Error> {
1585    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1586    /// controller.authenticate(None).await?;
1587    ///
1588    /// let streams = controller.get_streams().await?;
1589    /// for stream in streams {
1590    ///     println!("Stream {} -> {}:{} ({:?})",
1591    ///         stream.id, stream.target_host, stream.target_port, stream.status);
1592    /// }
1593    /// # Ok(())
1594    /// # }
1595    /// ```
1596    ///
1597    /// # See Also
1598    ///
1599    /// - [`Stream`]: Stream information structure
1600    /// - [`attach_stream`](Self::attach_stream): Attach a stream to a circuit
1601    /// - [`close_stream`](Self::close_stream): Close a stream
1602    pub async fn get_streams(&mut self) -> Result<Vec<Stream>, Error> {
1603        let response_str = self.get_info("stream-status").await?;
1604        parse_streams(&response_str)
1605    }
1606
1607    /// Attaches a stream to a specific circuit.
1608    ///
1609    /// Manually attaches a stream to a circuit. This is typically used when
1610    /// you want to control which circuit a stream uses, rather than letting
1611    /// Tor choose automatically.
1612    ///
1613    /// # Arguments
1614    ///
1615    /// * `stream_id` - The stream to attach
1616    /// * `circuit_id` - The circuit to attach the stream to
1617    ///
1618    /// # Preconditions
1619    ///
1620    /// - The stream must be in a state that allows attachment (typically NEW)
1621    /// - The circuit must be BUILT
1622    /// - The circuit's exit policy must allow the stream's target
1623    ///
1624    /// # Errors
1625    ///
1626    /// Returns [`Error::OperationFailed`] if:
1627    /// - The stream doesn't exist
1628    /// - The circuit doesn't exist
1629    /// - The stream is not in an attachable state
1630    /// - The circuit cannot handle the stream's target
1631    ///
1632    /// # Example
1633    ///
1634    /// ```rust,no_run
1635    /// use stem_rs::controller::{Controller, CircuitId, StreamId};
1636    ///
1637    /// # async fn example() -> Result<(), stem_rs::Error> {
1638    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1639    /// controller.authenticate(None).await?;
1640    ///
1641    /// // Attach stream 1 to circuit 5
1642    /// let stream_id = StreamId::new("1");
1643    /// let circuit_id = CircuitId::new("5");
1644    /// controller.attach_stream(&stream_id, &circuit_id).await?;
1645    /// # Ok(())
1646    /// # }
1647    /// ```
1648    ///
1649    /// # See Also
1650    ///
1651    /// - [`get_streams`](Self::get_streams): List active streams
1652    /// - [`close_stream`](Self::close_stream): Close a stream
1653    pub async fn attach_stream(
1654        &mut self,
1655        stream_id: &StreamId,
1656        circuit_id: &CircuitId,
1657    ) -> Result<(), Error> {
1658        let command = format!("ATTACHSTREAM {} {}", stream_id.0, circuit_id.0);
1659        self.socket.send(&command).await?;
1660        let response = self.recv_response().await?;
1661
1662        if response.is_ok() {
1663            Ok(())
1664        } else {
1665            Err(Error::OperationFailed {
1666                code: response.status_code.to_string(),
1667                message: response.content().to_string(),
1668            })
1669        }
1670    }
1671
1672    /// Closes an existing stream.
1673    ///
1674    /// Terminates the specified stream with an optional reason code.
1675    ///
1676    /// # Arguments
1677    ///
1678    /// * `id` - The stream ID to close
1679    /// * `reason` - Optional reason code (defaults to 1 = MISC if not specified)
1680    ///
1681    /// # Reason Codes
1682    ///
1683    /// Common reason codes include:
1684    /// - 1: MISC (miscellaneous)
1685    /// - 2: RESOLVEFAILED (DNS resolution failed)
1686    /// - 3: CONNECTREFUSED (connection refused)
1687    /// - 4: EXITPOLICY (exit policy violation)
1688    /// - 5: DESTROY (circuit destroyed)
1689    /// - 6: DONE (stream finished normally)
1690    /// - 7: TIMEOUT (connection timeout)
1691    ///
1692    /// # Errors
1693    ///
1694    /// Returns [`Error::OperationFailed`] if:
1695    /// - The stream doesn't exist
1696    /// - The stream is already closed
1697    /// - Tor returns an error response
1698    ///
1699    /// # Example
1700    ///
1701    /// ```rust,no_run
1702    /// use stem_rs::controller::{Controller, StreamId};
1703    ///
1704    /// # async fn example() -> Result<(), stem_rs::Error> {
1705    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1706    /// controller.authenticate(None).await?;
1707    ///
1708    /// // Close stream with default reason
1709    /// let stream_id = StreamId::new("1");
1710    /// controller.close_stream(&stream_id, None).await?;
1711    ///
1712    /// // Close stream with specific reason (DONE)
1713    /// controller.close_stream(&stream_id, Some(6)).await?;
1714    /// # Ok(())
1715    /// # }
1716    /// ```
1717    ///
1718    /// # See Also
1719    ///
1720    /// - [`get_streams`](Self::get_streams): List active streams
1721    /// - [`attach_stream`](Self::attach_stream): Attach a stream to a circuit
1722    pub async fn close_stream(&mut self, id: &StreamId, reason: Option<u8>) -> Result<(), Error> {
1723        let command = match reason {
1724            Some(r) => format!("CLOSESTREAM {} {}", id.0, r),
1725            None => format!("CLOSESTREAM {} 1", id.0),
1726        };
1727        self.socket.send(&command).await?;
1728        let response = self.recv_response().await?;
1729
1730        if response.is_ok() {
1731            Ok(())
1732        } else {
1733            Err(Error::OperationFailed {
1734                code: response.status_code.to_string(),
1735                message: response.content().to_string(),
1736            })
1737        }
1738    }
1739
1740    /// Maps one address to another for Tor connections.
1741    ///
1742    /// Creates an address mapping so that connections to the `from` address
1743    /// are redirected to the `to` address. This is useful for creating
1744    /// virtual addresses or redirecting traffic.
1745    ///
1746    /// # Arguments
1747    ///
1748    /// * `from` - The source address to map from
1749    /// * `to` - The destination address to map to
1750    ///
1751    /// # Returns
1752    ///
1753    /// Returns a `HashMap` containing the established mappings. The keys are
1754    /// the source addresses and values are the destination addresses.
1755    ///
1756    /// # Errors
1757    ///
1758    /// Returns [`Error::OperationFailed`] if:
1759    /// - The address format is invalid
1760    /// - The mapping cannot be created
1761    /// - Tor returns an error response
1762    ///
1763    /// # Example
1764    ///
1765    /// ```rust,no_run
1766    /// use stem_rs::controller::Controller;
1767    ///
1768    /// # async fn example() -> Result<(), stem_rs::Error> {
1769    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1770    /// controller.authenticate(None).await?;
1771    ///
1772    /// // Map a hostname to a .onion address
1773    /// let mappings = controller.map_address(
1774    ///     "www.example.com",
1775    ///     "exampleonion.onion"
1776    /// ).await?;
1777    ///
1778    /// for (from, to) in mappings {
1779    ///     println!("{} -> {}", from, to);
1780    /// }
1781    /// # Ok(())
1782    /// # }
1783    /// ```
1784    pub async fn map_address(
1785        &mut self,
1786        from: &str,
1787        to: &str,
1788    ) -> Result<HashMap<String, String>, Error> {
1789        let command = format!("MAPADDRESS {}={}", from, to);
1790        self.socket.send(&command).await?;
1791        let response = self.recv_response().await?;
1792
1793        if !response.is_ok() {
1794            return Err(Error::OperationFailed {
1795                code: response.status_code.to_string(),
1796                message: response.content().to_string(),
1797            });
1798        }
1799
1800        let mut mappings = HashMap::new();
1801        for line in &response.lines {
1802            if let Some(eq_pos) = line.find('=') {
1803                let key = line[..eq_pos].to_string();
1804                let value = line[eq_pos + 1..].to_string();
1805                mappings.insert(key, value);
1806            }
1807        }
1808        Ok(mappings)
1809    }
1810
1811    /// Subscribes to asynchronous events from Tor.
1812    ///
1813    /// Configures which event types Tor should send to this controller.
1814    /// Events are received via [`recv_event`](Self::recv_event).
1815    ///
1816    /// # Arguments
1817    ///
1818    /// * `events` - List of event types to subscribe to
1819    ///
1820    /// # Event Types
1821    ///
1822    /// Common event types include:
1823    /// - [`EventType::Circ`] - Circuit status changes
1824    /// - [`EventType::Stream`] - Stream status changes
1825    /// - [`EventType::Bw`] - Bandwidth usage
1826    /// - [`EventType::Notice`] - Notice-level log messages
1827    /// - [`EventType::Warn`] - Warning-level log messages
1828    ///
1829    /// # Errors
1830    ///
1831    /// Returns [`Error::OperationFailed`] if:
1832    /// - An event type is not recognized
1833    /// - Tor returns an error response
1834    ///
1835    /// # Example
1836    ///
1837    /// ```rust,no_run
1838    /// use stem_rs::controller::Controller;
1839    /// use stem_rs::EventType;
1840    ///
1841    /// # async fn example() -> Result<(), stem_rs::Error> {
1842    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1843    /// controller.authenticate(None).await?;
1844    ///
1845    /// // Subscribe to circuit and bandwidth events
1846    /// controller.set_events(&[EventType::Circ, EventType::Bw]).await?;
1847    ///
1848    /// // Receive events
1849    /// loop {
1850    ///     let event = controller.recv_event().await?;
1851    ///     println!("Received event: {:?}", event);
1852    /// }
1853    /// # Ok(())
1854    /// # }
1855    /// ```
1856    ///
1857    /// # Clearing Subscriptions
1858    ///
1859    /// To stop receiving events, call with an empty slice:
1860    ///
1861    /// ```rust,no_run
1862    /// # use stem_rs::controller::Controller;
1863    /// # async fn example() -> Result<(), stem_rs::Error> {
1864    /// # let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1865    /// controller.set_events(&[]).await?; // Clear all subscriptions
1866    /// # Ok(())
1867    /// # }
1868    /// ```
1869    ///
1870    /// # See Also
1871    ///
1872    /// - [`recv_event`](Self::recv_event): Receive subscribed events
1873    /// - [`EventType`]: Available event types
1874    /// - [`events`](crate::events): Event parsing module
1875    pub async fn set_events(&mut self, events: &[EventType]) -> Result<(), Error> {
1876        let event_names: Vec<String> = events.iter().map(|e| e.to_string()).collect();
1877        let command = if event_names.is_empty() {
1878            "SETEVENTS".to_string()
1879        } else {
1880            format!("SETEVENTS {}", event_names.join(" "))
1881        };
1882        self.socket.send(&command).await?;
1883        let response = self.recv_response().await?;
1884
1885        if response.is_ok() {
1886            Ok(())
1887        } else {
1888            Err(Error::OperationFailed {
1889                code: response.status_code.to_string(),
1890                message: response.content().to_string(),
1891            })
1892        }
1893    }
1894
1895    /// Receives the next asynchronous event from Tor.
1896    ///
1897    /// Blocks until an event is available. Events must first be subscribed
1898    /// to using [`set_events`](Self::set_events).
1899    ///
1900    /// # Event Buffering
1901    ///
1902    /// Events that arrive while waiting for command responses are automatically
1903    /// buffered and returned by subsequent calls to this method.
1904    ///
1905    /// # Errors
1906    ///
1907    /// Returns [`Error::Protocol`] if:
1908    /// - The received message is not an event (status code != 650)
1909    ///
1910    /// Returns [`Error::Socket`] if:
1911    /// - The connection is closed
1912    /// - A network error occurs
1913    ///
1914    /// # Example
1915    ///
1916    /// ```rust,no_run
1917    /// use stem_rs::controller::Controller;
1918    /// use stem_rs::EventType;
1919    /// use stem_rs::events::ParsedEvent;
1920    ///
1921    /// # async fn example() -> Result<(), stem_rs::Error> {
1922    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1923    /// controller.authenticate(None).await?;
1924    ///
1925    /// // Subscribe to bandwidth events
1926    /// controller.set_events(&[EventType::Bw]).await?;
1927    ///
1928    /// // Receive and process events
1929    /// loop {
1930    ///     match controller.recv_event().await? {
1931    ///         ParsedEvent::Bandwidth(bw) => {
1932    ///             println!("Bandwidth: {} read, {} written", bw.read, bw.written);
1933    ///         }
1934    ///         other => println!("Other event: {:?}", other),
1935    ///     }
1936    /// }
1937    /// # Ok(())
1938    /// # }
1939    /// ```
1940    ///
1941    /// # See Also
1942    ///
1943    /// - [`set_events`](Self::set_events): Subscribe to events
1944    /// - [`ParsedEvent`]: Event types
1945    pub async fn recv_event(&mut self) -> Result<ParsedEvent, Error> {
1946        let response = if let Some(buffered) = self.event_buffer.pop() {
1947            buffered
1948        } else {
1949            self.socket.recv().await?
1950        };
1951
1952        if response.status_code != 650 {
1953            return Err(Error::Protocol(format!(
1954                "expected async event (650), got {}",
1955                response.status_code
1956            )));
1957        }
1958
1959        let content = response.content();
1960        let (event_type, event_content) = content.split_once(' ').unwrap_or((content, ""));
1961
1962        let lines: Vec<String> = response
1963            .lines
1964            .iter()
1965            .skip(1)
1966            .filter(|l| !l.is_empty() && *l != "OK")
1967            .cloned()
1968            .collect();
1969
1970        ParsedEvent::parse(event_type, event_content, Some(&lines))
1971    }
1972
1973    /// Sends a raw command to Tor and returns the response.
1974    ///
1975    /// This is a low-level method for sending arbitrary control protocol
1976    /// commands. For most use cases, prefer the typed methods like
1977    /// [`get_info`](Self::get_info), [`signal`](Self::signal), etc.
1978    ///
1979    /// # Arguments
1980    ///
1981    /// * `command` - The raw command string to send
1982    ///
1983    /// # Errors
1984    ///
1985    /// Returns [`Error::OperationFailed`] if:
1986    /// - Tor returns an error response
1987    ///
1988    /// Returns [`Error::Socket`] if:
1989    /// - The connection is closed
1990    /// - A network error occurs
1991    ///
1992    /// # Example
1993    ///
1994    /// ```rust,no_run
1995    /// use stem_rs::controller::Controller;
1996    ///
1997    /// # async fn example() -> Result<(), stem_rs::Error> {
1998    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
1999    /// controller.authenticate(None).await?;
2000    ///
2001    /// // Send a raw GETINFO command
2002    /// let response = controller.msg("GETINFO version").await?;
2003    /// println!("Raw response: {}", response);
2004    /// # Ok(())
2005    /// # }
2006    /// ```
2007    ///
2008    /// # See Also
2009    ///
2010    /// - [`get_info`](Self::get_info): Typed GETINFO wrapper
2011    /// - [`signal`](Self::signal): Typed SIGNAL wrapper
2012    pub async fn msg(&mut self, command: &str) -> Result<String, Error> {
2013        self.socket.send(command).await?;
2014        let response = self.recv_response().await?;
2015
2016        if !response.is_ok() {
2017            return Err(Error::OperationFailed {
2018                code: response.status_code.to_string(),
2019                message: response.content().to_string(),
2020            });
2021        }
2022
2023        Ok(response.raw_content())
2024    }
2025
2026    /// Creates an ephemeral hidden service.
2027    ///
2028    /// Unlike file-based hidden services, ephemeral services don't touch disk
2029    /// and are the recommended way to create hidden services programmatically.
2030    ///
2031    /// # Arguments
2032    ///
2033    /// * `ports` - Mapping of virtual ports to local targets (e.g., `[(80, "127.0.0.1:8080")]`)
2034    /// * `key_type` - Type of key: `"NEW"` to generate, `"RSA1024"`, or `"ED25519-V3"`
2035    /// * `key_content` - Key content or type to generate (`"BEST"`, `"RSA1024"`, `"ED25519-V3"`)
2036    /// * `flags` - Optional flags like `"Detach"`, `"DiscardPK"`, `"BasicAuth"`, `"MaxStreamsCloseCircuit"`
2037    ///
2038    /// # Returns
2039    ///
2040    /// Returns an [`AddOnionResponse`] containing:
2041    /// - `service_id`: The onion address (without `.onion` suffix)
2042    /// - `private_key`: The private key (unless `DiscardPK` flag was set)
2043    /// - `private_key_type`: The key type (e.g., `"ED25519-V3"`)
2044    ///
2045    /// # Example
2046    ///
2047    /// ```rust,no_run
2048    /// use stem_rs::controller::Controller;
2049    ///
2050    /// # async fn example() -> Result<(), stem_rs::Error> {
2051    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2052    /// controller.authenticate(None).await?;
2053    ///
2054    /// // Create a v3 hidden service mapping port 80 to local port 8080
2055    /// let response = controller.create_ephemeral_hidden_service(
2056    ///     &[(80, "127.0.0.1:8080")],
2057    ///     "NEW",
2058    ///     "ED25519-V3",
2059    ///     &[],
2060    /// ).await?;
2061    ///
2062    /// println!("Hidden service: {}.onion", response.service_id);
2063    /// # Ok(())
2064    /// # }
2065    /// ```
2066    ///
2067    /// # See Also
2068    ///
2069    /// - [`remove_ephemeral_hidden_service`](Self::remove_ephemeral_hidden_service): Remove the service
2070    pub async fn create_ephemeral_hidden_service(
2071        &mut self,
2072        ports: &[(u16, &str)],
2073        key_type: &str,
2074        key_content: &str,
2075        flags: &[&str],
2076    ) -> Result<AddOnionResponse, Error> {
2077        let mut request = format!("ADD_ONION {}:{}", key_type, key_content);
2078
2079        if !flags.is_empty() {
2080            request.push_str(&format!(" Flags={}", flags.join(",")));
2081        }
2082
2083        for (virt_port, target) in ports {
2084            request.push_str(&format!(" Port={},{}", virt_port, target));
2085        }
2086
2087        self.socket.send(&request).await?;
2088        let response = self.recv_response().await?;
2089
2090        if !response.is_ok() {
2091            return Err(Error::OperationFailed {
2092                code: response.status_code.to_string(),
2093                message: response.content().to_string(),
2094            });
2095        }
2096
2097        parse_add_onion_response(&response.all_content())
2098    }
2099
2100    /// Removes an ephemeral hidden service.
2101    ///
2102    /// Discontinues a hidden service that was created with
2103    /// [`create_ephemeral_hidden_service`](Self::create_ephemeral_hidden_service).
2104    ///
2105    /// # Arguments
2106    ///
2107    /// * `service_id` - The onion address without the `.onion` suffix
2108    ///
2109    /// # Returns
2110    ///
2111    /// Returns `true` if the service was removed, `false` if it wasn't running.
2112    ///
2113    /// # Example
2114    ///
2115    /// ```rust,no_run
2116    /// use stem_rs::controller::Controller;
2117    ///
2118    /// # async fn example() -> Result<(), stem_rs::Error> {
2119    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2120    /// controller.authenticate(None).await?;
2121    ///
2122    /// // Create and then remove a hidden service
2123    /// let response = controller.create_ephemeral_hidden_service(
2124    ///     &[(80, "127.0.0.1:8080")],
2125    ///     "NEW",
2126    ///     "BEST",
2127    ///     &[],
2128    /// ).await?;
2129    ///
2130    /// controller.remove_ephemeral_hidden_service(&response.service_id).await?;
2131    /// # Ok(())
2132    /// # }
2133    /// ```
2134    pub async fn remove_ephemeral_hidden_service(&mut self, service_id: &str) -> Result<bool, Error> {
2135        let command = format!("DEL_ONION {}", service_id);
2136        match self.msg(&command).await {
2137            Ok(_) => Ok(true),
2138            Err(Error::OperationFailed { code, message }) => {
2139                if message.contains("Unknown Onion Service") {
2140                    Ok(false)
2141                } else {
2142                    Err(Error::OperationFailed { code, message })
2143                }
2144            }
2145            Err(e) => Err(e),
2146        }
2147    }
2148
2149    /// Loads configuration text as if it were read from the torrc.
2150    ///
2151    /// This allows dynamically configuring Tor without modifying the torrc file.
2152    /// The configuration text is processed as if it were part of the torrc.
2153    ///
2154    /// # Arguments
2155    ///
2156    /// * `config_text` - The configuration text to load
2157    ///
2158    /// # Errors
2159    ///
2160    /// Returns [`Error::InvalidRequest`] if:
2161    /// - The configuration text contains invalid options
2162    /// - The configuration text has syntax errors
2163    ///
2164    /// Returns [`Error::InvalidArguments`] if:
2165    /// - An unknown configuration option is specified
2166    ///
2167    /// # Example
2168    ///
2169    /// ```rust,no_run
2170    /// use stem_rs::controller::Controller;
2171    ///
2172    /// # async fn example() -> Result<(), stem_rs::Error> {
2173    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2174    /// controller.authenticate(None).await?;
2175    ///
2176    /// // Load configuration
2177    /// controller.load_conf("MaxCircuitDirtiness 600").await?;
2178    /// # Ok(())
2179    /// # }
2180    /// ```
2181    ///
2182    /// # See Also
2183    ///
2184    /// - [`set_conf`](Self::set_conf): Set individual configuration options
2185    /// - [`save_conf`](Self::save_conf): Save configuration to torrc
2186    pub async fn load_conf(&mut self, config_text: &str) -> Result<(), Error> {
2187        let command = format!("LOADCONF\n{}", config_text);
2188        self.socket.send(&command).await?;
2189        let response = self.recv_response().await?;
2190
2191        if response.is_ok() {
2192            Ok(())
2193        } else if response.status_code == 552 || response.status_code == 553 {
2194            let message = response.content().to_string();
2195            if response.status_code == 552 && message.contains("Unknown option") {
2196                // Extract the unknown option name
2197                Err(Error::InvalidArguments(message))
2198            } else {
2199                Err(Error::InvalidRequest(message))
2200            }
2201        } else {
2202            Err(Error::Protocol(format!(
2203                "LOADCONF received unexpected response: {}",
2204                response.status_code
2205            )))
2206        }
2207    }
2208
2209    /// Drops guard nodes and optionally resets circuit timeouts.
2210    ///
2211    /// This forces Tor to drop its current guard nodes and select new ones.
2212    /// Optionally, circuit build timeout counters can also be reset.
2213    ///
2214    /// # Arguments
2215    ///
2216    /// * `reset_timeouts` - If `true`, also reset circuit build timeout counters
2217    ///
2218    /// # Errors
2219    ///
2220    /// Returns [`Error::OperationFailed`] if:
2221    /// - Tor returns an error response
2222    /// - `reset_timeouts` is `true` but Tor version doesn't support DROPTIMEOUTS
2223    ///
2224    /// # Example
2225    ///
2226    /// ```rust,no_run
2227    /// use stem_rs::controller::Controller;
2228    ///
2229    /// # async fn example() -> Result<(), stem_rs::Error> {
2230    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2231    /// controller.authenticate(None).await?;
2232    ///
2233    /// // Drop guards only
2234    /// controller.drop_guards(false).await?;
2235    ///
2236    /// // Drop guards and reset timeouts
2237    /// controller.drop_guards(true).await?;
2238    /// # Ok(())
2239    /// # }
2240    /// ```
2241    pub async fn drop_guards(&mut self, reset_timeouts: bool) -> Result<(), Error> {
2242        self.socket.send("DROPGUARDS").await?;
2243        let response = self.recv_response().await?;
2244
2245        if !response.is_ok() {
2246            return Err(Error::OperationFailed {
2247                code: response.status_code.to_string(),
2248                message: response.content().to_string(),
2249            });
2250        }
2251
2252        if reset_timeouts {
2253            self.socket.send("DROPTIMEOUTS").await?;
2254            let response = self.recv_response().await?;
2255
2256            if !response.is_ok() {
2257                return Err(Error::OperationFailed {
2258                    code: response.status_code.to_string(),
2259                    message: response.content().to_string(),
2260                });
2261            }
2262        }
2263
2264        Ok(())
2265    }
2266
2267    /// Changes a circuit's purpose.
2268    ///
2269    /// Currently, two purposes are recognized: "general" and "controller".
2270    ///
2271    /// # Arguments
2272    ///
2273    /// * `circuit_id` - The ID of the circuit to repurpose
2274    /// * `purpose` - The new purpose for the circuit
2275    ///
2276    /// # Errors
2277    ///
2278    /// Returns [`Error::InvalidRequest`] if:
2279    /// - The circuit doesn't exist
2280    /// - The purpose is invalid
2281    ///
2282    /// # Example
2283    ///
2284    /// ```rust,no_run
2285    /// use stem_rs::controller::{Controller, CircuitId, CircuitPurpose};
2286    ///
2287    /// # async fn example() -> Result<(), stem_rs::Error> {
2288    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2289    /// controller.authenticate(None).await?;
2290    ///
2291    /// let circuit_id = CircuitId::new("5");
2292    /// controller.repurpose_circuit(&circuit_id, CircuitPurpose::Controller).await?;
2293    /// # Ok(())
2294    /// # }
2295    /// ```
2296    pub async fn repurpose_circuit(
2297        &mut self,
2298        circuit_id: &CircuitId,
2299        purpose: CircuitPurpose,
2300    ) -> Result<(), Error> {
2301        let command = format!("SETCIRCUITPURPOSE {} purpose={}", circuit_id.0, purpose);
2302        self.socket.send(&command).await?;
2303        let response = self.recv_response().await?;
2304
2305        if response.is_ok() {
2306            Ok(())
2307        } else if response.status_code == 552 {
2308            Err(Error::InvalidRequest(response.content().to_string()))
2309        } else {
2310            Err(Error::Protocol(format!(
2311                "SETCIRCUITPURPOSE returned unexpected response code: {}",
2312                response.status_code
2313            )))
2314        }
2315    }
2316
2317    /// Enables controller features that are disabled by default.
2318    ///
2319    /// Once enabled, a feature cannot be disabled and a new control connection
2320    /// must be opened to get a connection with the feature disabled.
2321    ///
2322    /// # Arguments
2323    ///
2324    /// * `features` - List of feature names to enable
2325    ///
2326    /// # Errors
2327    ///
2328    /// Returns [`Error::InvalidArguments`] if:
2329    /// - An unrecognized feature is specified
2330    ///
2331    /// # Example
2332    ///
2333    /// ```rust,no_run
2334    /// use stem_rs::controller::Controller;
2335    ///
2336    /// # async fn example() -> Result<(), stem_rs::Error> {
2337    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2338    /// controller.authenticate(None).await?;
2339    ///
2340    /// controller.enable_feature(&["VERBOSE_NAMES"]).await?;
2341    /// # Ok(())
2342    /// # }
2343    /// ```
2344    pub async fn enable_feature(&mut self, features: &[&str]) -> Result<(), Error> {
2345        let command = format!("USEFEATURE {}", features.join(" "));
2346        self.socket.send(&command).await?;
2347        let response = self.recv_response().await?;
2348
2349        if response.is_ok() {
2350            Ok(())
2351        } else if response.status_code == 552 {
2352            let message = response.content().to_string();
2353            Err(Error::InvalidArguments(message))
2354        } else {
2355            Err(Error::Protocol(format!(
2356                "USEFEATURE provided an invalid response code: {}",
2357                response.status_code
2358            )))
2359        }
2360    }
2361
2362    /// Gets the addresses and ports where Tor is listening for connections.
2363    ///
2364    /// Returns a list of (address, port) tuples for the specified listener type.
2365    ///
2366    /// # Arguments
2367    ///
2368    /// * `listener_type` - The type of listener to query
2369    ///
2370    /// # Errors
2371    ///
2372    /// Returns [`Error::OperationFailed`] if:
2373    /// - The GETINFO command fails
2374    ///
2375    /// Returns [`Error::Protocol`] if:
2376    /// - The response format is unexpected
2377    ///
2378    /// # Example
2379    ///
2380    /// ```rust,no_run
2381    /// use stem_rs::controller::{Controller, ListenerType};
2382    ///
2383    /// # async fn example() -> Result<(), stem_rs::Error> {
2384    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2385    /// controller.authenticate(None).await?;
2386    ///
2387    /// let listeners = controller.get_listeners(ListenerType::Socks).await?;
2388    /// for (addr, port) in listeners {
2389    ///     println!("SOCKS listener: {}:{}", addr, port);
2390    /// }
2391    /// # Ok(())
2392    /// # }
2393    /// ```
2394    ///
2395    /// # See Also
2396    ///
2397    /// - [`get_ports`](Self::get_ports): Get just the port numbers
2398    pub async fn get_listeners(
2399        &mut self,
2400        listener_type: ListenerType,
2401    ) -> Result<Vec<(String, u16)>, Error> {
2402        let query = format!("net/listeners/{}", listener_type);
2403        let response = self.get_info(&query).await?;
2404
2405        parse_listeners(&response)
2406    }
2407
2408    /// Gets just the port numbers where Tor is listening for connections.
2409    ///
2410    /// Returns a set of unique port numbers for the specified listener type.
2411    /// This is a convenience method that extracts just the ports from
2412    /// [`get_listeners`](Self::get_listeners).
2413    ///
2414    /// # Arguments
2415    ///
2416    /// * `listener_type` - The type of listener to query
2417    ///
2418    /// # Example
2419    ///
2420    /// ```rust,no_run
2421    /// use stem_rs::controller::{Controller, ListenerType};
2422    ///
2423    /// # async fn example() -> Result<(), stem_rs::Error> {
2424    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2425    /// controller.authenticate(None).await?;
2426    ///
2427    /// let ports = controller.get_ports(ListenerType::Socks).await?;
2428    /// for port in ports {
2429    ///     println!("SOCKS port: {}", port);
2430    /// }
2431    /// # Ok(())
2432    /// # }
2433    /// ```
2434    ///
2435    /// # See Also
2436    ///
2437    /// - [`get_listeners`](Self::get_listeners): Get addresses and ports
2438    pub async fn get_ports(&mut self, listener_type: ListenerType) -> Result<HashSet<u16>, Error> {
2439        let listeners = self.get_listeners(listener_type).await?;
2440        Ok(listeners.into_iter().map(|(_, port)| port).collect())
2441    }
2442
2443    /// Gets the user Tor is running as.
2444    ///
2445    /// # Errors
2446    ///
2447    /// Returns [`Error::OperationFailed`] if:
2448    /// - The information is not available
2449    /// - Tor returns an error response
2450    ///
2451    /// # Example
2452    ///
2453    /// ```rust,no_run
2454    /// use stem_rs::controller::Controller;
2455    ///
2456    /// # async fn example() -> Result<(), stem_rs::Error> {
2457    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2458    /// controller.authenticate(None).await?;
2459    ///
2460    /// let user = controller.get_user().await?;
2461    /// println!("Tor is running as: {}", user);
2462    /// # Ok(())
2463    /// # }
2464    /// ```
2465    pub async fn get_user(&mut self) -> Result<String, Error> {
2466        self.get_info("process/user").await
2467    }
2468
2469    /// Gets the Unix timestamp when Tor started.
2470    ///
2471    /// Calculates the start time by subtracting the uptime from the current time.
2472    ///
2473    /// # Errors
2474    ///
2475    /// Returns an error if:
2476    /// - The uptime cannot be determined
2477    /// - The uptime value is invalid
2478    ///
2479    /// # Example
2480    ///
2481    /// ```rust,no_run
2482    /// use stem_rs::controller::Controller;
2483    ///
2484    /// # async fn example() -> Result<(), stem_rs::Error> {
2485    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2486    /// controller.authenticate(None).await?;
2487    ///
2488    /// let start_time = controller.get_start_time().await?;
2489    /// println!("Tor started at: {}", start_time);
2490    /// # Ok(())
2491    /// # }
2492    /// ```
2493    ///
2494    /// # See Also
2495    ///
2496    /// - [`get_uptime`](Self::get_uptime): Get how long Tor has been running
2497    pub async fn get_start_time(&mut self) -> Result<f64, Error> {
2498        let uptime = self.get_uptime().await?;
2499        let now = SystemTime::now()
2500            .duration_since(UNIX_EPOCH)
2501            .map_err(|e| Error::Parse {
2502                location: "system time".to_string(),
2503                reason: e.to_string(),
2504            })?
2505            .as_secs_f64();
2506        Ok(now - uptime)
2507    }
2508
2509    /// Gets how long Tor has been running in seconds.
2510    ///
2511    /// # Errors
2512    ///
2513    /// Returns an error if:
2514    /// - The GETINFO command fails
2515    /// - The uptime value cannot be parsed
2516    ///
2517    /// # Example
2518    ///
2519    /// ```rust,no_run
2520    /// use stem_rs::controller::Controller;
2521    ///
2522    /// # async fn example() -> Result<(), stem_rs::Error> {
2523    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2524    /// controller.authenticate(None).await?;
2525    ///
2526    /// let uptime = controller.get_uptime().await?;
2527    /// println!("Tor has been running for {} seconds", uptime);
2528    /// # Ok(())
2529    /// # }
2530    /// ```
2531    ///
2532    /// # See Also
2533    ///
2534    /// - [`get_start_time`](Self::get_start_time): Get when Tor started
2535    pub async fn get_uptime(&mut self) -> Result<f64, Error> {
2536        let uptime_str = self.get_info("process/uptime").await?;
2537        uptime_str.parse().map_err(|_| Error::Parse {
2538            location: "uptime".to_string(),
2539            reason: format!("invalid uptime value: {}", uptime_str),
2540        })
2541    }
2542
2543    /// Gets protocol information including authentication methods.
2544    ///
2545    /// Returns information about the Tor control protocol version,
2546    /// the Tor version, and available authentication methods.
2547    ///
2548    /// # Errors
2549    ///
2550    /// Returns [`Error::Parse`] if:
2551    /// - The PROTOCOLINFO response cannot be parsed
2552    ///
2553    /// # Example
2554    ///
2555    /// ```rust,no_run
2556    /// use stem_rs::controller::Controller;
2557    ///
2558    /// # async fn example() -> Result<(), stem_rs::Error> {
2559    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2560    ///
2561    /// let info = controller.get_protocolinfo().await?;
2562    /// println!("Protocol version: {}", info.protocol_version);
2563    /// println!("Tor version: {}", info.tor_version);
2564    /// println!("Auth methods: {:?}", info.auth_methods);
2565    /// # Ok(())
2566    /// # }
2567    /// ```
2568    pub async fn get_protocolinfo(&mut self) -> Result<ProtocolInfo, Error> {
2569        self.socket.send("PROTOCOLINFO 1").await?;
2570        let response = self.recv_response().await?;
2571
2572        if !response.is_ok() {
2573            return Err(Error::OperationFailed {
2574                code: response.status_code.to_string(),
2575                message: response.content().to_string(),
2576            });
2577        }
2578
2579        parse_protocolinfo(&response.all_content())
2580    }
2581
2582    /// Gets accounting statistics for bandwidth limiting.
2583    ///
2584    /// Returns statistics about Tor's accounting status when AccountingMax
2585    /// is set in the torrc.
2586    ///
2587    /// # Errors
2588    ///
2589    /// Returns [`Error::OperationFailed`] if:
2590    /// - Accounting is not enabled
2591    /// - The GETINFO commands fail
2592    ///
2593    /// # Example
2594    ///
2595    /// ```rust,no_run
2596    /// use stem_rs::controller::Controller;
2597    ///
2598    /// # async fn example() -> Result<(), stem_rs::Error> {
2599    /// let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
2600    /// controller.authenticate(None).await?;
2601    ///
2602    /// let stats = controller.get_accounting_stats().await?;
2603    /// println!("Status: {}", stats.status);
2604    /// println!("Read: {} bytes", stats.read_bytes);
2605    /// println!("Written: {} bytes", stats.written_bytes);
2606    /// # Ok(())
2607    /// # }
2608    /// ```
2609    pub async fn get_accounting_stats(&mut self) -> Result<AccountingStats, Error> {
2610        // Check if accounting is enabled
2611        let enabled = self.get_info("accounting/enabled").await?;
2612        if enabled != "1" {
2613            return Err(Error::OperationFailed {
2614                code: "552".to_string(),
2615                message: "Accounting isn't enabled".to_string(),
2616            });
2617        }
2618
2619        let retrieved = SystemTime::now()
2620            .duration_since(UNIX_EPOCH)
2621            .map_err(|e| Error::Parse {
2622                location: "system time".to_string(),
2623                reason: e.to_string(),
2624            })?
2625            .as_secs_f64();
2626
2627        let status = self.get_info("accounting/hibernating").await?;
2628        let interval_end = self.get_info("accounting/interval-end").await.ok();
2629        let bytes = self.get_info("accounting/bytes").await?;
2630        let bytes_left = self.get_info("accounting/bytes-left").await?;
2631
2632        // Parse bytes: "read_bytes written_bytes"
2633        let (read_bytes, written_bytes) = parse_accounting_bytes(&bytes)?;
2634        let (read_bytes_left, write_bytes_left) = parse_accounting_bytes(&bytes_left)?;
2635
2636        // Calculate time until reset
2637        let time_until_reset = if let Some(ref end) = interval_end {
2638            parse_interval_end_to_seconds(end, retrieved).unwrap_or(0)
2639        } else {
2640            0
2641        };
2642
2643        Ok(AccountingStats {
2644            retrieved,
2645            status,
2646            interval_end,
2647            time_until_reset,
2648            read_bytes,
2649            read_bytes_left,
2650            read_limit: read_bytes + read_bytes_left,
2651            written_bytes,
2652            write_bytes_left,
2653            write_limit: written_bytes + write_bytes_left,
2654        })
2655    }
2656}
2657
2658/// Response from ADD_ONION command.
2659///
2660/// Contains the service ID and optionally the private key for the hidden service.
2661#[derive(Debug, Clone)]
2662pub struct AddOnionResponse {
2663    /// The onion address without the `.onion` suffix.
2664    pub service_id: String,
2665    /// The private key (base64 encoded), if not discarded.
2666    pub private_key: Option<String>,
2667    /// The type of private key (e.g., `"ED25519-V3"`, `"RSA1024"`).
2668    pub private_key_type: Option<String>,
2669}
2670
2671/// Parses the response from an ADD_ONION command.
2672fn parse_add_onion_response(content: &str) -> Result<AddOnionResponse, Error> {
2673    let mut service_id = None;
2674    let mut private_key = None;
2675    let mut private_key_type = None;
2676
2677    for line in content.lines() {
2678        let line = line.trim();
2679        if let Some(value) = line.strip_prefix("ServiceID=") {
2680            service_id = Some(value.to_string());
2681        } else if let Some(value) = line.strip_prefix("PrivateKey=") {
2682            if let Some((key_type, key_content)) = value.split_once(':') {
2683                private_key_type = Some(key_type.to_string());
2684                private_key = Some(key_content.to_string());
2685            }
2686        }
2687    }
2688
2689    let service_id = service_id.ok_or_else(|| Error::Parse {
2690        location: "ADD_ONION response".to_string(),
2691        reason: "missing ServiceID".to_string(),
2692    })?;
2693
2694    Ok(AddOnionResponse {
2695        service_id,
2696        private_key,
2697        private_key_type,
2698    })
2699}
2700
2701/// Parses circuit status output from GETINFO circuit-status.
2702///
2703/// Converts the multi-line circuit status response into a vector of [`Circuit`] structs.
2704fn parse_circuits(content: &str) -> Result<Vec<Circuit>, Error> {
2705    let mut circuits = Vec::new();
2706
2707    for line in content.lines() {
2708        let line = line.trim();
2709        if line.is_empty() {
2710            continue;
2711        }
2712
2713        let mut parts = line.split_whitespace();
2714        let id = parts.next().ok_or_else(|| Error::Parse {
2715            location: "circuit".to_string(),
2716            reason: "missing circuit id".to_string(),
2717        })?;
2718
2719        let status_str = parts.next().ok_or_else(|| Error::Parse {
2720            location: "circuit".to_string(),
2721            reason: "missing circuit status".to_string(),
2722        })?;
2723
2724        let status = parse_circ_status(status_str)?;
2725
2726        let mut path = Vec::new();
2727        if let Some(path_str) = parts.next() {
2728            if !path_str.starts_with("BUILD_FLAGS=")
2729                && !path_str.starts_with("PURPOSE=")
2730                && !path_str.starts_with("TIME_CREATED=")
2731            {
2732                for relay in path_str.split(',') {
2733                    let relay_info = parse_relay_info(relay);
2734                    path.push(relay_info);
2735                }
2736            }
2737        }
2738
2739        circuits.push(Circuit {
2740            id: CircuitId::new(id),
2741            status,
2742            path,
2743        });
2744    }
2745
2746    Ok(circuits)
2747}
2748
2749/// Parses a circuit status string into a [`CircStatus`] enum.
2750fn parse_circ_status(s: &str) -> Result<CircStatus, Error> {
2751    match s.to_uppercase().as_str() {
2752        "LAUNCHED" => Ok(CircStatus::Launched),
2753        "BUILT" => Ok(CircStatus::Built),
2754        "GUARD_WAIT" => Ok(CircStatus::GuardWait),
2755        "EXTENDED" => Ok(CircStatus::Extended),
2756        "FAILED" => Ok(CircStatus::Failed),
2757        "CLOSED" => Ok(CircStatus::Closed),
2758        _ => Err(Error::Parse {
2759            location: "circuit status".to_string(),
2760            reason: format!("unknown status: {}", s),
2761        }),
2762    }
2763}
2764
2765/// Parses a relay specification string into a [`RelayInfo`] struct.
2766///
2767/// Handles formats like `$FINGERPRINT~Nickname` or just `$FINGERPRINT`.
2768fn parse_relay_info(s: &str) -> RelayInfo {
2769    if let Some((fingerprint, nickname)) = s.split_once('~') {
2770        RelayInfo {
2771            fingerprint: fingerprint.trim_start_matches('$').to_string(),
2772            nickname: Some(nickname.to_string()),
2773        }
2774    } else {
2775        RelayInfo {
2776            fingerprint: s.trim_start_matches('$').to_string(),
2777            nickname: None,
2778        }
2779    }
2780}
2781
2782/// Parses stream status output from GETINFO stream-status.
2783///
2784/// Converts the multi-line stream status response into a vector of [`Stream`] structs.
2785fn parse_streams(content: &str) -> Result<Vec<Stream>, Error> {
2786    let mut streams = Vec::new();
2787
2788    for line in content.lines() {
2789        let line = line.trim();
2790        if line.is_empty() {
2791            continue;
2792        }
2793
2794        let mut parts = line.split_whitespace();
2795        let id = parts.next().ok_or_else(|| Error::Parse {
2796            location: "stream".to_string(),
2797            reason: "missing stream id".to_string(),
2798        })?;
2799
2800        let status_str = parts.next().ok_or_else(|| Error::Parse {
2801            location: "stream".to_string(),
2802            reason: "missing stream status".to_string(),
2803        })?;
2804
2805        let status = parse_stream_status(status_str)?;
2806
2807        let circuit_id_str = parts.next().ok_or_else(|| Error::Parse {
2808            location: "stream".to_string(),
2809            reason: "missing circuit id".to_string(),
2810        })?;
2811
2812        let circuit_id = if circuit_id_str == "0" {
2813            None
2814        } else {
2815            Some(CircuitId::new(circuit_id_str))
2816        };
2817
2818        let target = parts.next().ok_or_else(|| Error::Parse {
2819            location: "stream".to_string(),
2820            reason: "missing target".to_string(),
2821        })?;
2822
2823        let (target_host, target_port) = parse_target(target)?;
2824
2825        streams.push(Stream {
2826            id: StreamId::new(id),
2827            status,
2828            circuit_id,
2829            target_host,
2830            target_port,
2831        });
2832    }
2833
2834    Ok(streams)
2835}
2836
2837/// Parses a stream status string into a [`StreamStatus`] enum.
2838fn parse_stream_status(s: &str) -> Result<StreamStatus, Error> {
2839    match s.to_uppercase().as_str() {
2840        "NEW" => Ok(StreamStatus::New),
2841        "NEWRESOLVE" => Ok(StreamStatus::NewResolve),
2842        "REMAP" => Ok(StreamStatus::Remap),
2843        "SENTCONNECT" => Ok(StreamStatus::SentConnect),
2844        "SENTRESOLVE" => Ok(StreamStatus::SentResolve),
2845        "SUCCEEDED" => Ok(StreamStatus::Succeeded),
2846        "FAILED" => Ok(StreamStatus::Failed),
2847        "DETACHED" => Ok(StreamStatus::Detached),
2848        "CONTROLLER_WAIT" => Ok(StreamStatus::ControllerWait),
2849        "CLOSED" => Ok(StreamStatus::Closed),
2850        _ => Err(Error::Parse {
2851            location: "stream status".to_string(),
2852            reason: format!("unknown status: {}", s),
2853        }),
2854    }
2855}
2856
2857/// Parses a target address string into host and port components.
2858///
2859/// Handles formats like `host:port` or just `host` (port defaults to 0).
2860fn parse_target(target: &str) -> Result<(String, u16), Error> {
2861    if let Some(colon_pos) = target.rfind(':') {
2862        let host = target[..colon_pos].to_string();
2863        let port_str = &target[colon_pos + 1..];
2864        let port: u16 = port_str.parse().map_err(|_| Error::Parse {
2865            location: "stream target".to_string(),
2866            reason: format!("invalid port: {}", port_str),
2867        })?;
2868        Ok((host, port))
2869    } else {
2870        Ok((target.to_string(), 0))
2871    }
2872}
2873
2874/// Parses listener response from GETINFO net/listeners/*.
2875///
2876/// The response contains quoted "address:port" pairs separated by spaces.
2877fn parse_listeners(response: &str) -> Result<Vec<(String, u16)>, Error> {
2878    let mut listeners = Vec::new();
2879
2880    for listener in response.split_whitespace() {
2881        // Strip quotes
2882        let listener = listener.trim_matches('"');
2883        if listener.is_empty() {
2884            continue;
2885        }
2886
2887        // Skip unix sockets
2888        if listener.starts_with("unix:") {
2889            continue;
2890        }
2891
2892        // Find the last colon to split address and port
2893        if let Some(colon_pos) = listener.rfind(':') {
2894            let mut addr = listener[..colon_pos].to_string();
2895            let port_str = &listener[colon_pos + 1..];
2896
2897            // Handle IPv6 addresses in brackets
2898            if addr.starts_with('[') && addr.ends_with(']') {
2899                addr = addr[1..addr.len() - 1].to_string();
2900            }
2901
2902            let port: u16 = port_str.parse().map_err(|_| Error::Parse {
2903                location: "listener".to_string(),
2904                reason: format!("invalid port: {}", port_str),
2905            })?;
2906
2907            listeners.push((addr, port));
2908        }
2909    }
2910
2911    Ok(listeners)
2912}
2913
2914/// Parses PROTOCOLINFO response.
2915fn parse_protocolinfo(content: &str) -> Result<ProtocolInfo, Error> {
2916    let mut protocol_version = 1;
2917    let mut tor_version = String::new();
2918    let mut auth_methods = Vec::new();
2919    let mut cookie_file = None;
2920
2921    for line in content.lines() {
2922        let line = line.trim();
2923
2924        if let Some(stripped) = line.strip_prefix("PROTOCOLINFO ") {
2925            if let Ok(v) = stripped.trim().parse::<u32>() {
2926                protocol_version = v;
2927            }
2928        } else if let Some(rest) = line.strip_prefix("AUTH METHODS=") {
2929            // Parse: AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE="/path/to/cookie"
2930
2931            // Find METHODS value
2932            if let Some(space_pos) = rest.find(' ') {
2933                let methods_str = &rest[..space_pos];
2934                auth_methods = methods_str.split(',').map(|s| s.to_string()).collect();
2935
2936                // Look for COOKIEFILE
2937                let remaining = &rest[space_pos..];
2938                if let Some(cookie_start) = remaining.find("COOKIEFILE=\"") {
2939                    let cookie_path_start = cookie_start + 12;
2940                    if let Some(cookie_end) = remaining[cookie_path_start..].find('"') {
2941                        cookie_file =
2942                            Some(remaining[cookie_path_start..cookie_path_start + cookie_end].to_string());
2943                    }
2944                }
2945            } else {
2946                auth_methods = rest.split(',').map(|s| s.to_string()).collect();
2947            }
2948        } else if line.starts_with("VERSION Tor=\"") {
2949            // Parse: VERSION Tor="0.4.7.10"
2950            if let Some(start) = line.find("Tor=\"") {
2951                let version_start = start + 5;
2952                if let Some(end) = line[version_start..].find('"') {
2953                    tor_version = line[version_start..version_start + end].to_string();
2954                }
2955            }
2956        }
2957    }
2958
2959    Ok(ProtocolInfo {
2960        protocol_version,
2961        tor_version,
2962        auth_methods,
2963        cookie_file,
2964    })
2965}
2966
2967/// Parses accounting bytes response: "read_bytes written_bytes"
2968fn parse_accounting_bytes(bytes_str: &str) -> Result<(u64, u64), Error> {
2969    let parts: Vec<&str> = bytes_str.split_whitespace().collect();
2970    if parts.len() != 2 {
2971        return Err(Error::Parse {
2972            location: "accounting bytes".to_string(),
2973            reason: format!("expected 'read written', got: {}", bytes_str),
2974        });
2975    }
2976
2977    let read: u64 = parts[0].parse().map_err(|_| Error::Parse {
2978        location: "accounting bytes".to_string(),
2979        reason: format!("invalid read bytes: {}", parts[0]),
2980    })?;
2981
2982    let written: u64 = parts[1].parse().map_err(|_| Error::Parse {
2983        location: "accounting bytes".to_string(),
2984        reason: format!("invalid written bytes: {}", parts[1]),
2985    })?;
2986
2987    Ok((read, written))
2988}
2989
2990/// Parses interval end timestamp and calculates seconds until reset.
2991fn parse_interval_end_to_seconds(interval_end: &str, current_time: f64) -> Option<u64> {
2992    // interval_end format: "YYYY-MM-DD HH:MM:SS"
2993    // This is a simplified parser - in production you'd use chrono
2994    let parts: Vec<&str> = interval_end.split_whitespace().collect();
2995    if parts.len() != 2 {
2996        return None;
2997    }
2998
2999    let date_parts: Vec<&str> = parts[0].split('-').collect();
3000    let time_parts: Vec<&str> = parts[1].split(':').collect();
3001
3002    if date_parts.len() != 3 || time_parts.len() != 3 {
3003        return None;
3004    }
3005
3006    let year: i32 = date_parts[0].parse().ok()?;
3007    let month: u32 = date_parts[1].parse().ok()?;
3008    let day: u32 = date_parts[2].parse().ok()?;
3009    let hour: u32 = time_parts[0].parse().ok()?;
3010    let minute: u32 = time_parts[1].parse().ok()?;
3011    let second: u32 = time_parts[2].parse().ok()?;
3012
3013    // Simple calculation - days since epoch approximation
3014    // This is a rough estimate; for production use chrono crate
3015    let days_since_epoch = (year - 1970) * 365 + (year - 1969) / 4 + day_of_year(month, day) as i32;
3016    let end_timestamp =
3017        days_since_epoch as f64 * 86400.0 + hour as f64 * 3600.0 + minute as f64 * 60.0 + second as f64;
3018
3019    if end_timestamp > current_time {
3020        Some((end_timestamp - current_time) as u64)
3021    } else {
3022        Some(0)
3023    }
3024}
3025
3026/// Helper to calculate day of year (approximate).
3027fn day_of_year(month: u32, day: u32) -> u32 {
3028    let days_before_month = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
3029    if (1..=12).contains(&month) {
3030        days_before_month[(month - 1) as usize] + day
3031    } else {
3032        day
3033    }
3034}
3035
3036#[cfg(test)]
3037mod tests {
3038    use super::*;
3039
3040    #[test]
3041    fn test_circuit_id_display() {
3042        let id = CircuitId::new("123");
3043        assert_eq!(id.to_string(), "123");
3044    }
3045
3046    #[test]
3047    fn test_stream_id_display() {
3048        let id = StreamId::new("456");
3049        assert_eq!(id.to_string(), "456");
3050    }
3051
3052    #[test]
3053    fn test_parse_circ_status() {
3054        assert_eq!(parse_circ_status("LAUNCHED").unwrap(), CircStatus::Launched);
3055        assert_eq!(parse_circ_status("BUILT").unwrap(), CircStatus::Built);
3056        assert_eq!(
3057            parse_circ_status("GUARD_WAIT").unwrap(),
3058            CircStatus::GuardWait
3059        );
3060        assert_eq!(parse_circ_status("EXTENDED").unwrap(), CircStatus::Extended);
3061        assert_eq!(parse_circ_status("FAILED").unwrap(), CircStatus::Failed);
3062        assert_eq!(parse_circ_status("CLOSED").unwrap(), CircStatus::Closed);
3063        assert_eq!(parse_circ_status("launched").unwrap(), CircStatus::Launched);
3064        assert!(parse_circ_status("UNKNOWN").is_err());
3065    }
3066
3067    #[test]
3068    fn test_parse_stream_status() {
3069        assert_eq!(parse_stream_status("NEW").unwrap(), StreamStatus::New);
3070        assert_eq!(
3071            parse_stream_status("NEWRESOLVE").unwrap(),
3072            StreamStatus::NewResolve
3073        );
3074        assert_eq!(parse_stream_status("REMAP").unwrap(), StreamStatus::Remap);
3075        assert_eq!(
3076            parse_stream_status("SENTCONNECT").unwrap(),
3077            StreamStatus::SentConnect
3078        );
3079        assert_eq!(
3080            parse_stream_status("SENTRESOLVE").unwrap(),
3081            StreamStatus::SentResolve
3082        );
3083        assert_eq!(
3084            parse_stream_status("SUCCEEDED").unwrap(),
3085            StreamStatus::Succeeded
3086        );
3087        assert_eq!(parse_stream_status("FAILED").unwrap(), StreamStatus::Failed);
3088        assert_eq!(
3089            parse_stream_status("DETACHED").unwrap(),
3090            StreamStatus::Detached
3091        );
3092        assert_eq!(
3093            parse_stream_status("CONTROLLER_WAIT").unwrap(),
3094            StreamStatus::ControllerWait
3095        );
3096        assert_eq!(parse_stream_status("CLOSED").unwrap(), StreamStatus::Closed);
3097        assert!(parse_stream_status("UNKNOWN").is_err());
3098    }
3099
3100    #[test]
3101    fn test_parse_relay_info_with_nickname() {
3102        let info = parse_relay_info("$ABCD1234~MyRelay");
3103        assert_eq!(info.fingerprint, "ABCD1234");
3104        assert_eq!(info.nickname, Some("MyRelay".to_string()));
3105    }
3106
3107    #[test]
3108    fn test_parse_relay_info_without_nickname() {
3109        let info = parse_relay_info("$ABCD1234");
3110        assert_eq!(info.fingerprint, "ABCD1234");
3111        assert_eq!(info.nickname, None);
3112    }
3113
3114    #[test]
3115    fn test_parse_relay_info_no_dollar() {
3116        let info = parse_relay_info("ABCD1234~MyRelay");
3117        assert_eq!(info.fingerprint, "ABCD1234");
3118        assert_eq!(info.nickname, Some("MyRelay".to_string()));
3119    }
3120
3121    #[test]
3122    fn test_parse_target_with_port() {
3123        let (host, port) = parse_target("example.com:443").unwrap();
3124        assert_eq!(host, "example.com");
3125        assert_eq!(port, 443);
3126    }
3127
3128    #[test]
3129    fn test_parse_target_ipv4_with_port() {
3130        let (host, port) = parse_target("192.168.1.1:80").unwrap();
3131        assert_eq!(host, "192.168.1.1");
3132        assert_eq!(port, 80);
3133    }
3134
3135    #[test]
3136    fn test_parse_target_without_port() {
3137        let (host, port) = parse_target("example.com").unwrap();
3138        assert_eq!(host, "example.com");
3139        assert_eq!(port, 0);
3140    }
3141
3142    #[test]
3143    fn test_parse_circuits_empty() {
3144        let circuits = parse_circuits("").unwrap();
3145        assert!(circuits.is_empty());
3146    }
3147
3148    #[test]
3149    fn test_parse_circuits_single() {
3150        let content = "1 BUILT $AAAA~Guard,$BBBB~Middle,$CCCC~Exit";
3151        let circuits = parse_circuits(content).unwrap();
3152        assert_eq!(circuits.len(), 1);
3153        assert_eq!(circuits[0].id.0, "1");
3154        assert_eq!(circuits[0].status, CircStatus::Built);
3155        assert_eq!(circuits[0].path.len(), 3);
3156        assert_eq!(circuits[0].path[0].fingerprint, "AAAA");
3157        assert_eq!(circuits[0].path[0].nickname, Some("Guard".to_string()));
3158    }
3159
3160    #[test]
3161    fn test_parse_circuits_multiple() {
3162        let content = "1 BUILT $AAAA~Guard,$BBBB~Exit\n2 LAUNCHED\n3 EXTENDED $CCCC~Relay";
3163        let circuits = parse_circuits(content).unwrap();
3164        assert_eq!(circuits.len(), 3);
3165        assert_eq!(circuits[0].status, CircStatus::Built);
3166        assert_eq!(circuits[1].status, CircStatus::Launched);
3167        assert_eq!(circuits[2].status, CircStatus::Extended);
3168    }
3169
3170    #[test]
3171    fn test_parse_circuits_with_flags() {
3172        let content = "1 BUILT $AAAA~Guard BUILD_FLAGS=IS_INTERNAL PURPOSE=GENERAL";
3173        let circuits = parse_circuits(content).unwrap();
3174        assert_eq!(circuits.len(), 1);
3175        assert_eq!(circuits[0].path.len(), 1);
3176    }
3177
3178    #[test]
3179    fn test_parse_streams_empty() {
3180        let streams = parse_streams("").unwrap();
3181        assert!(streams.is_empty());
3182    }
3183
3184    #[test]
3185    fn test_parse_streams_single() {
3186        let content = "1 SUCCEEDED 5 www.example.com:443";
3187        let streams = parse_streams(content).unwrap();
3188        assert_eq!(streams.len(), 1);
3189        assert_eq!(streams[0].id.0, "1");
3190        assert_eq!(streams[0].status, StreamStatus::Succeeded);
3191        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("5")));
3192        assert_eq!(streams[0].target_host, "www.example.com");
3193        assert_eq!(streams[0].target_port, 443);
3194    }
3195
3196    #[test]
3197    fn test_parse_streams_no_circuit() {
3198        let content = "1 NEW 0 www.example.com:80";
3199        let streams = parse_streams(content).unwrap();
3200        assert_eq!(streams.len(), 1);
3201        assert_eq!(streams[0].circuit_id, None);
3202    }
3203
3204    #[test]
3205    fn test_parse_streams_multiple() {
3206        let content = "1 SUCCEEDED 5 www.example.com:443\n2 NEW 0 api.example.com:80";
3207        let streams = parse_streams(content).unwrap();
3208        assert_eq!(streams.len(), 2);
3209    }
3210
3211    #[test]
3212    fn test_circuit_id_equality() {
3213        let id1 = CircuitId::new("123");
3214        let id2 = CircuitId::new("123");
3215        let id3 = CircuitId::new("456");
3216        assert_eq!(id1, id2);
3217        assert_ne!(id1, id3);
3218    }
3219
3220    #[test]
3221    fn test_stream_id_equality() {
3222        let id1 = StreamId::new("123");
3223        let id2 = StreamId::new("123");
3224        let id3 = StreamId::new("456");
3225        assert_eq!(id1, id2);
3226        assert_ne!(id1, id3);
3227    }
3228}
3229
3230#[cfg(test)]
3231mod stem_tests {
3232    use super::*;
3233
3234    #[test]
3235    fn test_parse_circ_path_empty() {
3236        let circuits = parse_circuits("").unwrap();
3237        assert!(circuits.is_empty());
3238    }
3239
3240    #[test]
3241    fn test_parse_circ_path_with_fingerprint_and_nickname() {
3242        let content = "1 BUILT $999A226EBED397F331B612FE1E4CFAE5C1F201BA~piyaz";
3243        let circuits = parse_circuits(content).unwrap();
3244        assert_eq!(circuits.len(), 1);
3245        assert_eq!(circuits[0].path.len(), 1);
3246        assert_eq!(
3247            circuits[0].path[0].fingerprint,
3248            "999A226EBED397F331B612FE1E4CFAE5C1F201BA"
3249        );
3250        assert_eq!(circuits[0].path[0].nickname, Some("piyaz".to_string()));
3251    }
3252
3253    #[test]
3254    fn test_parse_circ_path_multiple_relays() {
3255        let content =
3256            "1 BUILT $E57A476CD4DFBD99B4EE52A100A58610AD6E80B9,$AAAA,$BBBB~PrivacyRepublic14";
3257        let circuits = parse_circuits(content).unwrap();
3258        assert_eq!(circuits.len(), 1);
3259        assert_eq!(circuits[0].path.len(), 3);
3260        assert_eq!(
3261            circuits[0].path[0].fingerprint,
3262            "E57A476CD4DFBD99B4EE52A100A58610AD6E80B9"
3263        );
3264        assert_eq!(circuits[0].path[0].nickname, None);
3265        assert_eq!(circuits[0].path[2].fingerprint, "BBBB");
3266        assert_eq!(
3267            circuits[0].path[2].nickname,
3268            Some("PrivacyRepublic14".to_string())
3269        );
3270    }
3271
3272    #[test]
3273    fn test_get_streams_parsing() {
3274        let content =
3275            "1 NEW 4 10.10.10.1:80\n2 SUCCEEDED 4 10.10.10.1:80\n3 SUCCEEDED 4 10.10.10.1:80";
3276        let streams = parse_streams(content).unwrap();
3277        assert_eq!(streams.len(), 3);
3278
3279        assert_eq!(streams[0].id.0, "1");
3280        assert_eq!(streams[0].status, StreamStatus::New);
3281        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("4")));
3282        assert_eq!(streams[0].target_host, "10.10.10.1");
3283        assert_eq!(streams[0].target_port, 80);
3284
3285        assert_eq!(streams[1].id.0, "2");
3286        assert_eq!(streams[1].status, StreamStatus::Succeeded);
3287
3288        assert_eq!(streams[2].id.0, "3");
3289        assert_eq!(streams[2].status, StreamStatus::Succeeded);
3290    }
3291
3292    #[test]
3293    fn test_circuit_status_parsing() {
3294        let test_cases = [
3295            ("LAUNCHED", CircStatus::Launched),
3296            ("BUILT", CircStatus::Built),
3297            ("GUARD_WAIT", CircStatus::GuardWait),
3298            ("EXTENDED", CircStatus::Extended),
3299            ("FAILED", CircStatus::Failed),
3300            ("CLOSED", CircStatus::Closed),
3301        ];
3302
3303        for (input, expected) in test_cases {
3304            assert_eq!(parse_circ_status(input).unwrap(), expected);
3305        }
3306    }
3307
3308    #[test]
3309    fn test_stream_status_parsing() {
3310        let test_cases = [
3311            ("NEW", StreamStatus::New),
3312            ("NEWRESOLVE", StreamStatus::NewResolve),
3313            ("REMAP", StreamStatus::Remap),
3314            ("SENTCONNECT", StreamStatus::SentConnect),
3315            ("SENTRESOLVE", StreamStatus::SentResolve),
3316            ("SUCCEEDED", StreamStatus::Succeeded),
3317            ("FAILED", StreamStatus::Failed),
3318            ("DETACHED", StreamStatus::Detached),
3319            ("CONTROLLER_WAIT", StreamStatus::ControllerWait),
3320            ("CLOSED", StreamStatus::Closed),
3321        ];
3322
3323        for (input, expected) in test_cases {
3324            assert_eq!(parse_stream_status(input).unwrap(), expected);
3325        }
3326    }
3327
3328    #[test]
3329    fn test_parse_target_various() {
3330        let test_cases = [
3331            ("www.example.com:443", ("www.example.com", 443)),
3332            ("192.168.1.1:80", ("192.168.1.1", 80)),
3333            ("10.10.10.1:8080", ("10.10.10.1", 8080)),
3334            ("[::1]:443", ("[::1]", 443)),
3335        ];
3336
3337        for (input, (expected_host, expected_port)) in test_cases {
3338            let (host, port) = parse_target(input).unwrap();
3339            assert_eq!(host, expected_host);
3340            assert_eq!(port, expected_port);
3341        }
3342    }
3343
3344    #[test]
3345    fn test_parse_circuits_with_build_flags() {
3346        let content = "1 BUILT $AAAA~Guard,$BBBB~Exit BUILD_FLAGS=IS_INTERNAL,NEED_CAPACITY PURPOSE=GENERAL TIME_CREATED=2023-01-01T00:00:00";
3347        let circuits = parse_circuits(content).unwrap();
3348        assert_eq!(circuits.len(), 1);
3349        assert_eq!(circuits[0].status, CircStatus::Built);
3350        assert_eq!(circuits[0].path.len(), 2);
3351    }
3352
3353    #[test]
3354    fn test_parse_circuits_launched_no_path() {
3355        let content = "1 LAUNCHED BUILD_FLAGS=NEED_CAPACITY PURPOSE=GENERAL";
3356        let circuits = parse_circuits(content).unwrap();
3357        assert_eq!(circuits.len(), 1);
3358        assert_eq!(circuits[0].status, CircStatus::Launched);
3359        assert!(circuits[0].path.is_empty());
3360    }
3361
3362    #[test]
3363    fn test_parse_streams_detached() {
3364        let content = "1 DETACHED 0 www.example.com:443";
3365        let streams = parse_streams(content).unwrap();
3366        assert_eq!(streams.len(), 1);
3367        assert_eq!(streams[0].status, StreamStatus::Detached);
3368        assert_eq!(streams[0].circuit_id, None);
3369    }
3370
3371    #[test]
3372    fn test_relay_info_parsing_variations() {
3373        let test_cases = [
3374            ("$ABCD1234~MyRelay", "ABCD1234", Some("MyRelay")),
3375            ("$ABCD1234", "ABCD1234", None),
3376            ("ABCD1234~MyRelay", "ABCD1234", Some("MyRelay")),
3377            ("ABCD1234", "ABCD1234", None),
3378        ];
3379
3380        for (input, expected_fp, expected_nick) in test_cases {
3381            let info = parse_relay_info(input);
3382            assert_eq!(info.fingerprint, expected_fp);
3383            assert_eq!(info.nickname, expected_nick.map(|s| s.to_string()));
3384        }
3385    }
3386
3387    #[test]
3388    fn test_circuit_id_hash() {
3389        use std::collections::HashSet;
3390        let mut set = HashSet::new();
3391        set.insert(CircuitId::new("1"));
3392        set.insert(CircuitId::new("2"));
3393        set.insert(CircuitId::new("1"));
3394        assert_eq!(set.len(), 2);
3395    }
3396
3397    #[test]
3398    fn test_stream_id_hash() {
3399        use std::collections::HashSet;
3400        let mut set = HashSet::new();
3401        set.insert(StreamId::new("1"));
3402        set.insert(StreamId::new("2"));
3403        set.insert(StreamId::new("1"));
3404        assert_eq!(set.len(), 2);
3405    }
3406
3407    #[test]
3408    fn test_parse_circuits_real_world_example() {
3409        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"#;
3410        let circuits = parse_circuits(content).unwrap();
3411        assert_eq!(circuits.len(), 1);
3412        assert_eq!(circuits[0].id.0, "7");
3413        assert_eq!(circuits[0].status, CircStatus::Built);
3414        assert_eq!(circuits[0].path.len(), 3);
3415        assert_eq!(
3416            circuits[0].path[0].nickname,
3417            Some("Quetzalcoatl".to_string())
3418        );
3419        assert_eq!(
3420            circuits[0].path[1].nickname,
3421            Some("DigiGesTor4e3".to_string())
3422        );
3423        assert_eq!(
3424            circuits[0].path[2].nickname,
3425            Some("torserversNet".to_string())
3426        );
3427    }
3428
3429    #[test]
3430    fn test_parse_streams_real_world_example() {
3431        let content =
3432            "42 SUCCEEDED 7 www.torproject.org:443 SOURCE_ADDR=127.0.0.1:12345 PURPOSE=USER";
3433        let streams = parse_streams(content).unwrap();
3434        assert_eq!(streams.len(), 1);
3435        assert_eq!(streams[0].id.0, "42");
3436        assert_eq!(streams[0].status, StreamStatus::Succeeded);
3437        assert_eq!(streams[0].circuit_id, Some(CircuitId::new("7")));
3438        assert_eq!(streams[0].target_host, "www.torproject.org");
3439        assert_eq!(streams[0].target_port, 443);
3440    }
3441
3442    #[test]
3443    fn test_parse_add_onion_response_v3() {
3444        let content = "ServiceID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nPrivateKey=ED25519-V3:base64keydata==";
3445        let response = parse_add_onion_response(content).unwrap();
3446        assert_eq!(response.service_id, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
3447        assert_eq!(response.private_key_type, Some("ED25519-V3".to_string()));
3448        assert_eq!(response.private_key, Some("base64keydata==".to_string()));
3449    }
3450
3451    #[test]
3452    fn test_parse_add_onion_response_discarded_key() {
3453        let content = "ServiceID=abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv";
3454        let response = parse_add_onion_response(content).unwrap();
3455        assert_eq!(response.service_id, "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv");
3456        assert!(response.private_key.is_none());
3457        assert!(response.private_key_type.is_none());
3458    }
3459
3460    #[test]
3461    fn test_parse_add_onion_response_missing_service_id() {
3462        let content = "PrivateKey=ED25519-V3:base64keydata==";
3463        let result = parse_add_onion_response(content);
3464        assert!(result.is_err());
3465    }
3466
3467    #[test]
3468    fn test_listener_type_display() {
3469        assert_eq!(ListenerType::Or.to_string(), "or");
3470        assert_eq!(ListenerType::Dir.to_string(), "dir");
3471        assert_eq!(ListenerType::Socks.to_string(), "socks");
3472        assert_eq!(ListenerType::Trans.to_string(), "trans");
3473        assert_eq!(ListenerType::Natd.to_string(), "natd");
3474        assert_eq!(ListenerType::Dns.to_string(), "dns");
3475        assert_eq!(ListenerType::Control.to_string(), "control");
3476        assert_eq!(ListenerType::ExtOr.to_string(), "extor");
3477        assert_eq!(ListenerType::HttpTunnel.to_string(), "httptunnel");
3478    }
3479
3480    #[test]
3481    fn test_circuit_purpose_display() {
3482        assert_eq!(CircuitPurpose::General.to_string(), "general");
3483        assert_eq!(CircuitPurpose::Controller.to_string(), "controller");
3484    }
3485
3486    #[test]
3487    fn test_parse_listeners_single() {
3488        let response = r#""127.0.0.1:9050""#;
3489        let listeners = parse_listeners(response).unwrap();
3490        assert_eq!(listeners.len(), 1);
3491        assert_eq!(listeners[0], ("127.0.0.1".to_string(), 9050));
3492    }
3493
3494    #[test]
3495    fn test_parse_listeners_multiple() {
3496        let response = r#""127.0.0.1:9050" "0.0.0.0:9051""#;
3497        let listeners = parse_listeners(response).unwrap();
3498        assert_eq!(listeners.len(), 2);
3499        assert_eq!(listeners[0], ("127.0.0.1".to_string(), 9050));
3500        assert_eq!(listeners[1], ("0.0.0.0".to_string(), 9051));
3501    }
3502
3503    #[test]
3504    fn test_parse_listeners_ipv6() {
3505        let response = r#""[::1]:9050""#;
3506        let listeners = parse_listeners(response).unwrap();
3507        assert_eq!(listeners.len(), 1);
3508        assert_eq!(listeners[0], ("::1".to_string(), 9050));
3509    }
3510
3511    #[test]
3512    fn test_parse_listeners_empty() {
3513        let response = "";
3514        let listeners = parse_listeners(response).unwrap();
3515        assert!(listeners.is_empty());
3516    }
3517
3518    #[test]
3519    fn test_parse_listeners_unix_socket_skipped() {
3520        let response = r#""unix:/tmp/tor/socket" "127.0.0.1:9050""#;
3521        let listeners = parse_listeners(response).unwrap();
3522        assert_eq!(listeners.len(), 1);
3523        assert_eq!(listeners[0], ("127.0.0.1".to_string(), 9050));
3524    }
3525
3526    #[test]
3527    fn test_parse_protocolinfo_basic() {
3528        let content = r#"PROTOCOLINFO 1
3529AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE="/var/run/tor/control.authcookie"
3530VERSION Tor="0.4.7.10"
3531OK"#;
3532        let info = parse_protocolinfo(content).unwrap();
3533        assert_eq!(info.protocol_version, 1);
3534        assert_eq!(info.tor_version, "0.4.7.10");
3535        assert_eq!(info.auth_methods, vec!["COOKIE", "SAFECOOKIE"]);
3536        assert_eq!(
3537            info.cookie_file,
3538            Some("/var/run/tor/control.authcookie".to_string())
3539        );
3540    }
3541
3542    #[test]
3543    fn test_parse_protocolinfo_null_auth() {
3544        let content = r#"PROTOCOLINFO 1
3545AUTH METHODS=NULL
3546VERSION Tor="0.4.7.10"
3547OK"#;
3548        let info = parse_protocolinfo(content).unwrap();
3549        assert_eq!(info.auth_methods, vec!["NULL"]);
3550        assert!(info.cookie_file.is_none());
3551    }
3552
3553    #[test]
3554    fn test_parse_accounting_bytes() {
3555        let bytes_str = "1234567 7654321";
3556        let (read, written) = parse_accounting_bytes(bytes_str).unwrap();
3557        assert_eq!(read, 1234567);
3558        assert_eq!(written, 7654321);
3559    }
3560
3561    #[test]
3562    fn test_parse_accounting_bytes_zero() {
3563        let bytes_str = "0 0";
3564        let (read, written) = parse_accounting_bytes(bytes_str).unwrap();
3565        assert_eq!(read, 0);
3566        assert_eq!(written, 0);
3567    }
3568
3569    #[test]
3570    fn test_parse_accounting_bytes_invalid() {
3571        let bytes_str = "invalid";
3572        assert!(parse_accounting_bytes(bytes_str).is_err());
3573    }
3574
3575    #[test]
3576    fn test_listener_type_equality() {
3577        assert_eq!(ListenerType::Socks, ListenerType::Socks);
3578        assert_ne!(ListenerType::Socks, ListenerType::Control);
3579    }
3580
3581    #[test]
3582    fn test_circuit_purpose_equality() {
3583        assert_eq!(CircuitPurpose::General, CircuitPurpose::General);
3584        assert_ne!(CircuitPurpose::General, CircuitPurpose::Controller);
3585    }
3586
3587    #[test]
3588    fn test_listener_type_hash() {
3589        let mut set = HashSet::new();
3590        set.insert(ListenerType::Socks);
3591        set.insert(ListenerType::Control);
3592        set.insert(ListenerType::Socks);
3593        assert_eq!(set.len(), 2);
3594    }
3595}