Relay

Struct Relay 

Source
pub struct Relay {
    pub link_protocol: LinkProtocol,
    /* private fields */
}
Expand description

A connection to a Tor relay’s ORPort.

Relay represents an established TLS connection to a Tor relay and provides methods for creating circuits and communicating through them. This is the primary interface for direct relay communication.

§Conceptual Role

While Controller communicates with Tor via the control protocol, Relay implements the relay protocol for direct communication with Tor relays. This enables:

  • Fetching descriptors directly from relays
  • Creating circuits without a local Tor instance
  • Low-level relay protocol experimentation

§Connection Lifecycle

  1. Call Relay::connect to establish a TLS connection
  2. Link protocol is automatically negotiated (VERSIONS cell exchange)
  3. Connection is initialized (NETINFO cell exchange)
  4. Create circuits with Relay::create_circuit
  5. Close with Relay::close when done

§Invariants

  • The TLS connection remains valid while the Relay exists
  • Circuit IDs are unique and monotonically increasing
  • The negotiated link protocol determines cell format

§Thread Safety

Relay is Send but not Sync. The internal TLS stream is wrapped in Arc<Mutex<_>> allowing the relay to be moved between tasks, but concurrent access requires external synchronization.

§Example

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

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

println!("Connected with link protocol {}", relay.link_protocol.version);
println!("Connection established at {:?}", relay.connection_time());

// Create and use circuits...
let circuit = relay.create_circuit().await?;

relay.close().await?;

§Security

  • TLS certificates are not verified (relays use self-signed certificates)
  • The connection should be considered authenticated only after circuit creation
  • Key material for circuits is stored in memory

Fields§

§link_protocol: LinkProtocol

The negotiated link protocol for this connection.

Determines cell format, circuit ID size, and other protocol details. Higher versions generally provide more features.

Implementations§

Source§

impl Relay

Source

pub async fn connect( address: &str, port: u16, link_protocols: &[u32], ) -> Result<Self, Error>

Establishes a connection with a Tor relay’s ORPort.

Creates a TLS connection to the specified relay and negotiates the link protocol version. The connection is ready for circuit creation upon successful return.

§Protocol Negotiation

The connection process follows these steps:

  1. Establish TCP connection to address:port
  2. Perform TLS handshake (certificate verification disabled)
  3. Send VERSIONS cell with supported link_protocols
  4. Receive VERSIONS cell from relay
  5. Select highest mutually supported protocol version
  6. Send NETINFO cell to complete handshake
§Arguments
  • address - IP address or hostname of the relay
  • port - ORPort number (typically 9001 or 443)
  • link_protocols - Acceptable link protocol versions (e.g., &[3, 4, 5])
§Errors

Returns Error::InvalidArguments if:

  • link_protocols is empty

Returns Error::Protocol if:

  • TCP connection fails (relay unreachable or not an ORPort)
  • TLS handshake fails (SSL authentication error)
  • No common link protocol version exists
  • VERSIONS cell exchange fails
§Example
use stem_rs::client::{Relay, DEFAULT_LINK_PROTOCOLS};

// Connect with default protocols
let relay = Relay::connect("192.168.1.1", 9001, DEFAULT_LINK_PROTOCOLS).await?;

// Connect with specific protocols only
let relay = Relay::connect("192.168.1.1", 9001, &[5]).await?;
§Security

TLS certificate verification is disabled because Tor relays use self-signed certificates. The relay’s identity is verified through other means during circuit creation.

Source

pub fn is_alive(&self) -> bool

Checks if the relay connection is currently alive.

Returns whether the underlying TLS connection is still open and usable. Note that this is a simple check and may not detect all connection issues (e.g., the remote end closing the connection).

§Returns

true if the connection appears to be alive, false otherwise.

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

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

if relay.is_alive() {
    println!("Connection is active");
}
Source

pub fn connection_time(&self) -> Instant

Returns the time when this connection was established.

Provides the Instant when the TLS connection was successfully established. This can be used to track connection age or implement connection timeout logic.

§Returns

The Instant when Relay::connect completed successfully.

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

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

// Check connection age
let age = relay.connection_time().elapsed();
if age > Duration::from_secs(3600) {
    println!("Connection is over an hour old");
}
Source

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

Closes the relay connection.

Shuts down the underlying TLS connection. Any circuits created through this relay will become unusable after this call. This method should be called when the relay connection is no longer needed.

§Errors

Returns an error if the TLS shutdown fails. The connection may already be closed in this case.

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

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

// Use the relay...

// Clean up when done
relay.close().await?;
Source

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

Creates a new circuit through this relay.

Establishes a circuit using the CREATE_FAST/CREATED_FAST cell exchange. This is a simplified circuit creation method that doesn’t provide forward secrecy but is faster than the full CREATE2 handshake.

§Circuit Creation Process
  1. Generate random key material for CREATE_FAST cell
  2. Send CREATE_FAST cell with new circuit ID
  3. Receive CREATED_FAST cell with relay’s key material
  4. Derive encryption keys using KDF-TOR
  5. Verify relay knows the shared key (derivative key check)
§Returns

A RelayCircuit that can be used for communication through this relay.

§Errors

Returns Error::Protocol if:

  • The relay doesn’t respond with CREATED_FAST
  • The derivative key verification fails (relay doesn’t know shared key)
  • I/O errors occur during cell exchange
§Example
use stem_rs::client::{Relay, DEFAULT_LINK_PROTOCOLS};

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

// Create a circuit
let mut circuit = relay.create_circuit().await?;
println!("Created circuit with ID {}", circuit.id);

// Use the circuit for directory requests
let response = circuit.directory("GET /tor/server/authority HTTP/1.0\r\n\r\n", 1).await?;

// Clean up
circuit.close().await?;
§Security

CREATE_FAST is intended for creating the first hop of a circuit to a guard relay. It doesn’t provide forward secrecy because the key exchange is not authenticated with a long-term key. For multi-hop circuits, subsequent hops should use CREATE2 (not yet implemented).

Auto Trait Implementations§

§

impl Freeze for Relay

§

impl !RefUnwindSafe for Relay

§

impl Send for Relay

§

impl Sync for Relay

§

impl Unpin for Relay

§

impl !UnwindSafe for Relay

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> 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, 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.