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