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