RelayCircuit

Struct RelayCircuit 

Source
pub struct RelayCircuit {
    pub id: u32,
    /* private fields */
}
Expand description

A circuit established through a Tor relay.

RelayCircuit represents an established circuit through a Relay and provides methods for sending and receiving encrypted data. Circuits are the fundamental unit of communication in the Tor network.

§Conceptual Role

A circuit is a path through one or more Tor relays. This implementation supports single-hop circuits created with CREATE_FAST. Each circuit has:

  • A unique circuit ID within the relay connection
  • Forward and backward encryption keys
  • Forward and backward digests for integrity checking

§Encryption Semantics

Data sent through the circuit is encrypted using AES-CTR mode:

  • Forward direction: Client → Relay (uses forward_key)
  • Backward direction: Relay → Client (uses backward_key)

Each direction also maintains a running SHA-1 digest for integrity verification of relay cells.

§Invariants

  • Circuit ID is unique within the parent relay connection
  • Encryption keys are derived from the CREATE_FAST handshake
  • The circuit remains valid until explicitly closed

§Thread Safety

RelayCircuit is Clone and Send. Multiple clones share the same underlying relay connection through Arc<Mutex<_>>. Operations are serialized through the mutex.

§Example

use stem_rs::client::{Relay, DEFAULT_LINK_PROTOCOLS};

let mut relay = Relay::connect("127.0.0.1", 9001, DEFAULT_LINK_PROTOCOLS).await?;
let mut circuit = relay.create_circuit().await?;

// Make a directory request
let request = "GET /tor/server/authority HTTP/1.0\r\n\r\n";
let response = circuit.directory(request, 1).await?;

println!("Received {} bytes", response.len());

circuit.close().await?;

§Security

  • Keys are stored in memory for the circuit’s lifetime
  • Encryption is not yet fully implemented (placeholder)
  • Single-hop circuits don’t provide anonymity

Fields§

§id: u32

The unique identifier for this circuit within the relay connection.

Circuit IDs are assigned sequentially starting from the link protocol’s first_circ_id value. The ID is used in all cells sent on this circuit.

Implementations§

Source§

impl RelayCircuit

Source

pub async fn directory( &mut self, request: &str, stream_id: u16, ) -> Result<Vec<u8>, Error>

Sends a directory request through this circuit and returns the response.

This method establishes a directory stream and sends an HTTP request to fetch descriptor data from the relay. The relay must support the DirPort functionality for this to work.

§Protocol
  1. Send RELAY_BEGIN_DIR cell to open a directory stream
  2. Send RELAY_DATA cell with the HTTP request
  3. Receive RELAY_DATA cells with the response
  4. Stream ends when RELAY_END cell is received
§Arguments
  • request - HTTP request string (e.g., "GET /tor/server/authority HTTP/1.0\r\n\r\n")
  • stream_id - Stream identifier for this request (must be non-zero)
§Returns

The raw HTTP response bytes, including headers and body.

§Errors

Returns Error::Protocol if:

  • Response is for a different circuit ID
  • Unexpected cell type is received
  • I/O errors occur during communication
§Example
use stem_rs::client::{Relay, DEFAULT_LINK_PROTOCOLS};

let mut relay = Relay::connect("127.0.0.1", 9001, DEFAULT_LINK_PROTOCOLS).await?;
let mut circuit = relay.create_circuit().await?;

// Fetch the relay's server descriptor
let request = "GET /tor/server/authority HTTP/1.0\r\n\r\n";
let response = circuit.directory(request, 1).await?;

// Parse the HTTP response
let response_str = String::from_utf8_lossy(&response);
println!("{}", response_str);
§Common Requests
  • /tor/server/authority - Relay’s own server descriptor
  • /tor/server/fp/<fingerprint> - Server descriptor by fingerprint
  • /tor/status-vote/current/consensus - Current consensus document
Source

pub async fn close(&mut self) -> Result<(), Error>

Closes this circuit.

Sends a DESTROY cell to the relay to tear down the circuit. After calling this method, the circuit can no longer be used for communication.

§Errors

Returns an error if the DESTROY cell cannot be sent.

§Example
use stem_rs::client::{Relay, DEFAULT_LINK_PROTOCOLS};

let mut relay = Relay::connect("127.0.0.1", 9001, DEFAULT_LINK_PROTOCOLS).await?;
let mut circuit = relay.create_circuit().await?;

// Use the circuit...

// Close when done
circuit.close().await?;

Trait Implementations§

Source§

impl Clone for RelayCircuit

Source§

fn clone(&self) -> RelayCircuit

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.