pub struct ClientCirc { /* private fields */ }
Expand description

A circuit that we have constructed over the Tor network.

§Circuit life cycle

ClientCircs are created in an initially unusable state using Channel::new_circ, which returns a PendingClientCirc. To get a real (one-hop) circuit from one of these, you invoke one of its create_firsthop methods (currently create_firsthop_fast() or create_firsthop_ntor()). Then, to add more hops to the circuit, you can call extend_ntor() on it.

For higher-level APIs, see the tor-circmgr crate: the ones here in tor-proto are probably not what you need.

After a circuit is created, it will persist until it is closed in one of five ways:

  1. A remote error occurs.
  2. Some hop on the circuit sends a DESTROY message to tear down the circuit.
  3. The circuit’s channel is closed.
  4. Someone calls ClientCirc::terminate on the circuit.
  5. The last reference to the ClientCirc is dropped. (Note that every stream on a ClientCirc keeps a reference to it, which will in turn keep the circuit from closing until all those streams have gone away.)

Note that in cases 1-4 the ClientCirc object itself will still exist: it will just be unusable for most purposes. Most operations on it will fail with an error.

Implementations§

source§

impl ClientCirc

source

pub fn first_hop(&self) -> OwnedChanTarget

Return a description of the first hop of this circuit.

§Panics

Panics if there is no first hop. (This should be impossible outside of the tor-proto crate, but within the crate it’s possible to have a circuit with no hops.)

source

pub fn last_hop_num(&self) -> Result<HopNum>

Return the HopNum of the last hop of this circuit.

Returns an error if there is no last hop. (This should be impossible outside of the tor-proto crate, but within the crate it’s possible to have a circuit with no hops.)

source

pub fn path(&self) -> Vec<OwnedChanTarget>

👎Deprecated since 0.11.1: Use path_ref() instead.

Return a description of all the hops in this circuit.

This method is deprecated for several reasons:

  • It performs a deep copy.
  • It ignores virtual hops.
  • It’s not so extensible.

Use ClientCirc::path_ref() instead.

source

pub fn path_ref(&self) -> Arc<Path>

Return a Path object describing all the hops in this circuit.

Note that this Path is not automatically updated if the circuit is extended.

source

pub fn channel(&self) -> &Channel

Return a reference to the channel that this circuit is connected to.

A client circuit is always connected to some relay via a Channel. That relay has to be the same relay as the first hop in the client’s path.

source

pub fn binding_key(&self, hop: HopNum) -> Option<CircuitBinding>

Return the cryptographic material used to prove knowledge of a shared secret with with hop.

See CircuitBinding for more information on how this is used.

Return None if we have no circuit binding information for the hop, or if the hop does not exist.

source

pub async fn start_conversation( &self, msg: Option<AnyRelayMsg>, reply_handler: impl MsgHandler + Send + 'static, hop_num: HopNum ) -> Result<Conversation<'_>>

Available on crate feature send-control-msg only.

Start an ad-hoc protocol exchange to the specified hop on this circuit

To use this:

  1. Create an inter-task channel you’ll use to receive the outcome of your conversation, and bundle it into a MsgHandler.

  2. Call start_conversation. This will install a your handler, for incoming messages, and send the outgoing message (if you provided one). After that, each message on the circuit that isn’t handled by the core machinery is passed to your provided reply_handler.

  3. Possibly call send_msg on the Conversation, from the call site of start_conversation, possibly multiple times, from time to time, to send further desired messages to the peer.

  4. In your MsgHandler, process the incoming messages. You may respond by sending additional messages (using the ConversationInHandler provided to MsgHandler::handle_msg, or, outside the handler using the Conversation) When the protocol exchange is finished, MsgHandler::handle_msg should return ConversationFinished.

If you don’t need the Conversation to send followup messages, you may simply drop it, and rely on the responses you get from your handler, on the channel from step 0 above. Your handler will remain installed and able to process incoming messages until it returns ConversationFinished.

(If you don’t want to accept any replies at all, it may be simpler to use ClientCirc::send_raw_msg.)

Note that it is quite possible to use this function to violate the tor protocol; most users of this API will not need to call it. It is used to implement most of the onion service handshake.

§Limitations

Only one conversation may be active at any one time, for any one circuit. This generally means that this function should not be called on a circuit which might be shared with anyone else.

Likewise, it is forbidden to try to extend the circuit, while the conversation is in progress.

After the conversation has finished, the circuit may be extended. Or, start_conversation may be called again; but, in that case there will be a gap between the two conversations, during which no MsgHandler is installed, and unexpected incoming messages would close the circuit.

If these restrictions are violated, the circuit will be closed with an error.

§Precise definition of the lifetime of a conversation

A conversation is in progress from entry to start_conversation, until entry to the body of the MsgHandler::handle_msg call which returns ConversationFinished. (Entry since handle_msg is synchronously embedded into the incoming message processing.) So you may start a new conversation as soon as you have the final response via your inter-task channel from (0) above.

The lifetime relationship of the Conversation, vs the handler returning ConversationFinished is not enforced by the type system.

source

pub async fn start_conversation_last_hop( &self, msg: Option<AnyRelayMsg>, reply_handler: impl MsgHandler + Send + 'static ) -> Result<Conversation<'_>>

👎Deprecated since 0.13.0: Use start_conversation instead.
Available on crate feature send-control-msg only.

Start an ad-hoc protocol exchange to the final hop on this circuit

See the ClientCirc::start_conversation docs for more information.

source

pub async fn send_raw_msg( &self, msg: AnyRelayMsg, hop_num: HopNum ) -> Result<()>

Available on crate feature send-control-msg only.

Send an ad-hoc message to a given hop on the circuit, without expecting a reply.

(If you want to handle one or more possible replies, see ClientCirc::start_conversation.)

source

pub async fn allow_stream_requests( self: &Arc<ClientCirc>, allow_commands: &[RelayCmd], hop_num: HopNum ) -> Result<impl Stream<Item = IncomingStream>>

Available on crate feature hs-service only.

Tell this circuit to begin allowing the final hop of the circuit to try to create new Tor streams, and to return those pending requests in an asynchronous stream.

Ordinarily, these requests are rejected.

There can only be one Stream of this type created on a given circuit. If a such a Stream already exists, this method will return an error.

After this method has been called on a circuit, the circuit is expected to receive requests of this type indefinitely, until it is finally closed. If the Stream is dropped, the next request on this circuit will cause it to close.

Only onion services (and eventually) exit relays should call this method.

source

pub async fn extend_ntor<Tg>( &self, target: &Tg, params: &CircParameters ) -> Result<()>
where Tg: CircTarget,

Extend the circuit via the ntor handshake to a new target last hop.

source

pub async fn extend_ntor_v3<Tg>( &self, target: &Tg, params: &CircParameters ) -> Result<()>
where Tg: CircTarget,

Available on crate feature ntor_v3 only.

Extend the circuit via the ntor handshake to a new target last hop.

source

pub async fn extend_virtual( &self, protocol: RelayProtocol, role: HandshakeRole, seed: impl KeyGenerator, params: CircParameters ) -> Result<()>

Available on crate feature hs-common only.

Extend this circuit by a single, “virtual” hop.

A virtual hop is one for which we do not add an actual network connection between separate hosts (such as Relays). We only add a layer of cryptography.

This is used to implement onion services: the client and the service both build a circuit to a single rendezvous point, and tell the rendezvous point to relay traffic between their two circuits. Having completed a handshake out of band1, the parties each extend their circuits by a single “virtual” encryption hop that represents their shared cryptographic context.

Once a circuit has been extended in this way, it is an error to try to extend it in any other way.


  1. Technically, the handshake is only mostly out of band: the client sends their half of the handshake in an message, and the service's response is inline in itsRENDEZVOUS2` message. 

source

pub async fn begin_stream( self: &Arc<ClientCirc>, target: &str, port: u16, parameters: Option<StreamParameters> ) -> Result<DataStream>

Start a stream to the given address and port, using a BEGIN cell.

The use of a string for the address is intentional: you should let the remote Tor relay do the hostname lookup for you.

source

pub async fn begin_dir_stream(self: Arc<ClientCirc>) -> Result<DataStream>

Start a new stream to the last relay in the circuit, using a BEGIN_DIR cell.

source

pub async fn resolve( self: &Arc<ClientCirc>, hostname: &str ) -> Result<Vec<IpAddr>>

Perform a DNS lookup, using a RESOLVE cell with the last relay in this circuit.

Note that this function does not check for timeouts; that’s the caller’s responsibility.

source

pub async fn resolve_ptr( self: &Arc<ClientCirc>, addr: IpAddr ) -> Result<Vec<String>>

Perform a reverse DNS lookup, by sending a RESOLVE cell with the last relay on this circuit.

Note that this function does not check for timeouts; that’s the caller’s responsibility.

source

pub fn terminate(&self)

Shut down this circuit, along with all streams that are using it. Happens asynchronously (i.e. the circuit won’t necessarily be done shutting down immediately after this function returns!).

Note that other references to this circuit may exist. If they do, they will stop working after you call this function.

It’s not necessary to call this method if you’re just done with a circuit: the circuit should close on its own once nothing is using it any more.

source

pub fn is_closing(&self) -> bool

Return true if this circuit is closed and therefore unusable.

source

pub fn unique_id(&self) -> UniqId

Return a process-unique identifier for this circuit.

source

pub fn n_hops(&self) -> usize

Return the number of hops in this circuit.

NOTE: This function will currently return only the number of hops currently in the circuit. If there is an extend operation in progress, the currently pending hop may or may not be counted, depending on whether the extend operation finishes before this call is done.

source

pub fn wait_for_close(&self) -> impl Future<Output = ()> + Send + Sync + 'static

Available on crate feature experimental-api only.

Return a future that will resolve once this circuit has closed.

Note that this method does not itself cause the circuit to shut down.

TODO: Perhaps this should return some kind of status indication instead of just ()

Trait Implementations§

source§

impl Debug for ClientCirc

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. 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
§

impl<T> Conv for T

§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
§

impl<T> FmtForward for T

§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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.

§

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

§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
source§

impl<T> Same for T

§

type Output = T

Should always be Self
§

impl<T> Tap for T

§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
§

impl<T> TryConv for T

§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
source§

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

§

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

§

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

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more