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
- Call
Relay::connectto establish a TLS connection - Link protocol is automatically negotiated (VERSIONS cell exchange)
- Connection is initialized (NETINFO cell exchange)
- Create circuits with
Relay::create_circuit - Close with
Relay::closewhen done
§Invariants
- The TLS connection remains valid while the
Relayexists - 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: LinkProtocolThe 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
impl Relay
Sourcepub async fn connect(
address: &str,
port: u16,
link_protocols: &[u32],
) -> Result<Self, Error>
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:
- Establish TCP connection to
address:port - Perform TLS handshake (certificate verification disabled)
- Send VERSIONS cell with supported
link_protocols - Receive VERSIONS cell from relay
- Select highest mutually supported protocol version
- Send NETINFO cell to complete handshake
§Arguments
address- IP address or hostname of the relayport- ORPort number (typically 9001 or 443)link_protocols- Acceptable link protocol versions (e.g.,&[3, 4, 5])
§Errors
Returns Error::InvalidArguments if:
link_protocolsis 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.
Sourcepub fn is_alive(&self) -> bool
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");
}Sourcepub fn connection_time(&self) -> Instant
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");
}Sourcepub async fn close(&mut self) -> Result<(), Error>
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?;Sourcepub async fn create_circuit(&mut self) -> Result<RelayCircuit, Error>
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
- Generate random key material for CREATE_FAST cell
- Send CREATE_FAST cell with new circuit ID
- Receive CREATED_FAST cell with relay’s key material
- Derive encryption keys using KDF-TOR
- 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).