tor_circmgr/
tunnel.rs

1//! Code related to tunnel object that wraps the tor-proto tunnel.
2//!
3//! These tunnel types are part of the public API.
4
5use derive_deftly::{Deftly, define_derive_deftly};
6use std::{net::IpAddr, sync::Arc};
7
8use tor_cell::relaycell::msg::AnyRelayMsg;
9use tor_error::internal;
10use tor_linkspec::{CircTarget, IntoOwnedChanTarget, OwnedChanTarget};
11use tor_proto::{
12    ClockSkew, TargetHop,
13    circuit::UniqId,
14    client::circuit::{CircParameters, CircuitBinding, ClientCirc},
15    client::stream::{DataStream, StreamParameters},
16};
17use tracing::instrument;
18
19use crate::{Error, Result};
20
21#[cfg(feature = "hs-common")]
22use tor_proto::client::circuit::handshake;
23
24// The tunnel base methods. This MUST be derived on all tunnel types.
25define_derive_deftly! {
26    BaseTunnel for struct:
27
28    impl From<tor_proto::ClientTunnel> for $ttype {
29        fn from(tunnel: tor_proto::ClientTunnel) -> Self {
30            Self { tunnel: Arc::new(tunnel) }
31        }
32    }
33
34    impl From<Arc<tor_proto::ClientTunnel>> for $ttype {
35        fn from(tunnel: Arc<tor_proto::ClientTunnel>) -> Self {
36            Self { tunnel }
37        }
38    }
39
40    impl AsRef<tor_proto::ClientTunnel> for $ttype {
41        fn as_ref(&self) -> &tor_proto::ClientTunnel {
42            self.tunnel.as_ref()
43        }
44    }
45
46    impl $ttype {
47        /// Return a reference to the underlying tunnel.
48        ///
49        /// Note: The "tunnel" name is hardcoded here. If it becomes an annoyance, we could make it
50        /// as a meta value of the deftly declaration.
51        fn tunnel_ref(&self) -> &Arc<tor_proto::ClientTunnel> {
52            &self.tunnel
53        }
54
55        /// Return true if this tunnel is closed and therefore unusable.
56        pub fn is_closed(&self) -> bool {
57            self.tunnel_ref().is_closed()
58        }
59
60        /// Return a [`TargetHop`] representing precisely the last hop of the circuit as in set as a
61        /// HopLocation with its id and hop number.
62        ///
63        /// Return an error if there is no last hop.
64        pub fn last_hop(&self) -> Result<TargetHop> {
65            self.tunnel_ref().last_hop()
66                .map_err(|error| Error::Protocol {
67                    action: "get last hop",
68                    peer: None,
69                    error,
70                    unique_id: Some(self.tunnel.unique_id()),
71                })
72        }
73
74        /// Shutdown the tunnel meaning this sends a shutdown command to the underlying circuit
75        /// reactor which will stop asynchronously.
76        ///
77        /// Note that it is not necessary to use this method as in if the tunnel reference is
78        /// dropped, the circuit will close automatically.
79        pub fn terminate(&self) {
80            self.tunnel_ref().terminate();
81        }
82
83        /// Return a process-unique identifier for this tunnel.
84        pub fn unique_id(&self) -> UniqId {
85            self.tunnel_ref().unique_id()
86        }
87
88        /// Send raw message.
89        #[cfg(feature = "send-control-msg")]
90        pub async fn send_raw_msg(&self, msg: AnyRelayMsg, hop: TargetHop) -> Result<()> {
91            self.tunnel_ref()
92                .send_raw_msg(msg, hop)
93                .await
94                .map_err(|error| Error::Protocol {
95                    action: "send raw msg",
96                    peer: None,
97                    error,
98                    unique_id: Some(self.tunnel.unique_id()),
99                })
100        }
101
102        /// Start an ad-hoc protocol exchange to the specified hop on this tunnel.
103        ///
104        /// See [`ClientTunnel::start_conversation`](tor_proto::ClientTunnel::start_conversation)
105        /// documentation for more details.
106        #[cfg(feature = "send-control-msg")]
107        pub async fn start_conversation(&self,
108            msg: Option<tor_cell::relaycell::msg::AnyRelayMsg>,
109            reply_handler: impl tor_proto::MsgHandler + Send + 'static,
110            hop: TargetHop
111        ) -> Result<tor_proto::Conversation<'_>> {
112            self.tunnel_ref().start_conversation(msg, reply_handler, hop).await
113                .map_err(|error| Error::Protocol {
114                    action: "start conversation",
115                    peer: None,
116                    error,
117                    unique_id: Some(self.tunnel_ref().unique_id()),
118                })
119        }
120
121        /// Return a future that will resolve once this circuit has closed.
122        ///
123        /// Note that this method does not itself cause the circuit to shut down.
124        ///
125        // TODO: Perhaps this should return some kind of status indication instead
126        // of just ()
127        pub fn wait_for_close(&self) -> impl futures::Future<Output = ()> + Send + Sync + 'static + use<> {
128            self.tunnel_ref().wait_for_close()
129        }
130
131        // TODO(conflux): mq_account() is not needed because it is only used internally in a ClientCirc
132        // in order to open streams. It might be the case that we need at some point to get the
133        // CircuitAccount(s) from a tunnel. We would need then to either have a TunnelAccount or return
134        // a Vec<CircuitAccount>.
135    }
136}
137
138// Methods for a single path tunnel.
139define_derive_deftly! {
140    SinglePathTunnel for struct:
141
142    impl $ttype {
143        /// Return a reference to the circuit of this tunnel.
144        fn circuit(&self) -> Result<&ClientCirc> {
145            Ok(self.tunnel_ref()
146                .as_single_circ()
147                .map_err(|e| internal!("Non single path in a single path tunnel: {}", e))?)
148        }
149
150        /// Extend the circuit to a new target last hop using the ntor v3 handshake.
151        ///
152        /// TODO: Might want to pass which handshake type as a parameter so this function can be a
153        /// catch all on all possible handshakes. For now, use ntor v3 for all the things.
154        pub async fn extend<T: CircTarget>(&self, target: &T, params: CircParameters) -> Result<()> {
155            self.circuit()?
156                .extend(target, params)
157                .await
158                .map_err(|error| Error::Protocol {
159                    action: "extend tunnel",
160                    peer: Some(target.to_owned().to_logged()),
161                    error,
162                    unique_id: Some(self.tunnel.unique_id()),
163                })
164        }
165
166        /// Return the number of hops of the underlying circuit.
167        pub fn n_hops(&self) -> Result<usize> {
168            self.circuit()?.n_hops()
169                .map_err(|error| Error::Protocol {
170                    action: "get number hops",
171                    peer: None,
172                    error,
173                    unique_id: Some(self.tunnel_ref().unique_id()),
174                })
175        }
176
177    }
178}
179
180// Methods for a multi path tunnel.
181#[cfg(feature = "conflux")]
182define_derive_deftly! {
183    MultiPathTunnel for struct:
184
185    impl $ttype {
186        // TODO(conflux)
187        //
188        // As we add multi path support accross the code, we'll might need or not some specific
189        // functions that would go here.
190    }
191}
192
193// Methods for a tunnel that can transmit data (BEGIN).
194define_derive_deftly! {
195    DataTunnel for struct:
196
197    impl $ttype {
198        /// Start a stream to the given address and port, using a BEGIN cell.
199        ///
200        /// The use of a string for the address is intentional: you should let
201        /// the remote Tor relay do the hostname lookup for you.
202        #[instrument(skip_all, level = "trace")]
203        pub async fn begin_stream(
204            &self,
205            target: &str,
206            port: u16,
207            params: Option<StreamParameters>,
208        ) -> Result<DataStream> {
209            self.tunnel_ref()
210                .begin_stream(target, port, params)
211                .await
212                .map_err(|error| Error::Protocol {
213                    action: "begin stream",
214                    peer: None,
215                    error,
216                    unique_id: Some(self.tunnel.unique_id()),
217                })
218        }
219    }
220
221}
222
223// Methods for a tunnel that can do DNS resolution (RESOLVE).
224define_derive_deftly! {
225    DnsTunnel for struct:
226
227    impl $ttype {
228        /// Perform a DNS lookup, using a RESOLVE cell with the last relay in this circuit.
229        ///
230        /// Note that this function does not check for timeouts; that's the caller's responsibility.
231        pub async fn resolve(&self, hostname: &str) -> Result<Vec<IpAddr>> {
232            self.tunnel_ref()
233                .resolve(hostname)
234                .await
235                .map_err(|error| Error::Protocol {
236                    action: "resolve",
237                    peer: None,
238                    error,
239                    unique_id: Some(self.tunnel.unique_id()),
240                })
241        }
242
243        /// Perform a reverse DNS lookup, using a RESOLVE cell with the last relay in this circuit.
244        ///
245        /// Note that this function does not check for timeouts; that's the caller's responsibility.
246        pub async fn resolve_ptr(&self, addr: IpAddr) -> Result<Vec<String>> {
247            self.tunnel_ref()
248                .resolve_ptr(addr)
249                .await
250                .map_err(|error| Error::Protocol {
251                    action: "resolve PTR",
252                    peer: None,
253                    error,
254                    unique_id: Some(self.tunnel.unique_id()),
255                })
256        }
257    }
258}
259
260// Methods for a tunnel that can do directory requests (BEGIN_DIR).
261define_derive_deftly! {
262    DirTunnel for struct:
263
264    impl $ttype {
265        /// Start a stream to the given address and port, using a BEGIN_DIR cell.
266        pub async fn begin_dir_stream(&self) -> Result<DataStream> {
267            self.tunnel_ref().clone()
268                .begin_dir_stream()
269                .await
270                .map_err(|error| Error::Protocol {
271                    action: "begin dir stream",
272                    peer: None,
273                    error,
274                    unique_id: Some(self.tunnel.unique_id()),
275                })
276        }
277    }
278}
279
280// Methods for a tunnel that can transmit data (BEGIN).
281define_derive_deftly! {
282    OnionServiceDataTunnel for struct:
283
284    impl $ttype {
285        /// Extend this circuit by a single, "virtual" hop.
286        ///
287        /// A virtual hop is one for which we do not add an actual network connection
288        /// between separate hosts (such as Relays).  We only add a layer of
289        /// cryptography.
290        ///
291        /// This is used to implement onion services: the client and the service
292        /// both build a circuit to a single rendezvous point, and tell the
293        /// rendezvous point to relay traffic between their two circuits.  Having
294        /// completed a [`handshake`] out of band[^1], the parties each extend their
295        /// circuits by a single "virtual" encryption hop that represents their
296        /// shared cryptographic context.
297        ///
298        /// Once a circuit has been extended in this way, it is an error to try to
299        /// extend it in any other way.
300        ///
301        /// [^1]: Technically, the handshake is only _mostly_ out of band: the
302        ///     client sends their half of the handshake in an ` message, and the
303        ///     service's response is inline in its `RENDEZVOUS2` message.
304        //
305        // TODO hs: let's try to enforce the "you can't extend a circuit again once
306        // it has been extended this way" property.  We could do that with internal
307        // state, or some kind of a type state pattern.
308        //
309        // TODO hs: possibly we should take a set of Protovers, and not just `Params`.
310        #[cfg(feature = "hs-common")]
311        pub async fn extend_virtual(
312            &self,
313            protocol: handshake::RelayProtocol,
314            role: handshake::HandshakeRole,
315            seed: impl handshake::KeyGenerator,
316            params: CircParameters,
317            capabilities: &tor_protover::Protocols,
318        ) -> Result<()> {
319            self.circuit()?
320                .extend_virtual(protocol, role, seed, &params, capabilities)
321                .await
322                .map_err(|error| Error::Protocol {
323                    action: "extend virtual tunnel",
324                    peer: None,
325                    error,
326                    unique_id: Some(self.tunnel.unique_id()),
327                })
328        }
329    }
330
331}
332
333/// A client single path data tunnel.
334#[derive(Debug, Deftly)]
335#[derive_deftly(BaseTunnel, DataTunnel, DnsTunnel, SinglePathTunnel)]
336pub struct ClientDataTunnel {
337    /// The protocol level tunnel.
338    tunnel: Arc<tor_proto::ClientTunnel>,
339}
340
341/// A client directory tunnel. This is always single path.
342#[derive(Debug, Deftly)]
343#[derive_deftly(BaseTunnel, DirTunnel, SinglePathTunnel)]
344pub struct ClientDirTunnel {
345    /// The protocol level tunnel.
346    tunnel: Arc<tor_proto::ClientTunnel>,
347}
348
349/// A client onion service single path data tunnel.
350#[derive(Debug, Deftly)]
351#[derive_deftly(BaseTunnel, DataTunnel, OnionServiceDataTunnel, SinglePathTunnel)]
352pub struct ClientOnionServiceDataTunnel {
353    /// The protocol level tunnel.
354    tunnel: Arc<tor_proto::ClientTunnel>,
355}
356
357/// A client onion service directory tunnel (to an HSDir). This is always single path.
358#[derive(Debug, Deftly)]
359#[derive_deftly(BaseTunnel, DirTunnel, SinglePathTunnel)]
360pub struct ClientOnionServiceDirTunnel {
361    /// The protocol level tunnel.
362    tunnel: Arc<tor_proto::ClientTunnel>,
363}
364
365/// A client onion service introduction tunnel. This is always single path.
366#[derive(Debug, Deftly)]
367#[derive_deftly(BaseTunnel, SinglePathTunnel)]
368pub struct ClientOnionServiceIntroTunnel {
369    /// The protocol level tunnel.
370    tunnel: Arc<tor_proto::ClientTunnel>,
371}
372
373/// A service onion service single path data tunnel.
374#[derive(Debug, Deftly)]
375#[derive_deftly(BaseTunnel, DataTunnel, OnionServiceDataTunnel, SinglePathTunnel)]
376pub struct ServiceOnionServiceDataTunnel {
377    /// The protocol level tunnel.
378    tunnel: Arc<tor_proto::ClientTunnel>,
379}
380
381/// A service onion service directory tunnel (to an HSDir). This is always single path.
382#[derive(Debug, Deftly)]
383#[derive_deftly(BaseTunnel, DirTunnel, SinglePathTunnel)]
384pub struct ServiceOnionServiceDirTunnel {
385    /// The protocol level tunnel.
386    tunnel: Arc<tor_proto::ClientTunnel>,
387}
388
389/// A service onion service introduction tunnel. This is always single path.
390#[derive(Debug, Deftly)]
391#[derive_deftly(BaseTunnel, SinglePathTunnel)]
392pub struct ServiceOnionServiceIntroTunnel {
393    /// The protocol level tunnel.
394    tunnel: Arc<tor_proto::ClientTunnel>,
395}
396
397/// A client multi path data tunnel (Conflux).
398#[cfg(feature = "conflux")]
399#[derive(Debug, Deftly)]
400#[derive_deftly(BaseTunnel, DataTunnel, DnsTunnel, MultiPathTunnel)]
401pub struct ClientMultiPathDataTunnel {
402    /// The protocol level tunnel.
403    tunnel: Arc<tor_proto::ClientTunnel>,
404}
405
406/// A client multi path onion service data tunnel (Conflux, Rendeszvous).
407#[cfg(feature = "conflux")]
408#[derive(Debug, Deftly)]
409#[derive_deftly(BaseTunnel, DataTunnel, MultiPathTunnel)]
410pub struct ClientMultiPathOnionServiceDataTunnel {
411    /// The protocol level tunnel.
412    tunnel: Arc<tor_proto::ClientTunnel>,
413}
414
415/// A service multi path onion service data tunnel (Conflux, Rendeszvous).
416#[cfg(feature = "conflux")]
417#[derive(Debug, Deftly)]
418#[derive_deftly(BaseTunnel, DataTunnel, MultiPathTunnel)]
419pub struct ServiceMultiPathOnionServiceDataTunnel {
420    /// The protocol level tunnel.
421    tunnel: Arc<tor_proto::ClientTunnel>,
422}
423
424impl ClientDirTunnel {
425    /// Return a description of the first hop of this circuit.
426    pub fn first_hop(&self) -> OwnedChanTarget {
427        self.tunnel_ref()
428            .first_hop()
429            .expect("Bug getting dir tunnel first hop")
430    }
431
432    /// Get the clock skew claimed by the first hop of the circuit.
433    ///
434    /// See [`Channel::clock_skew()`](tor_proto::channel::Channel::clock_skew).
435    pub async fn first_hop_clock_skew(&self) -> Result<ClockSkew> {
436        // TODO(conflux): Is this CircCanceled error right?
437        self.circuit()?
438            .first_hop_clock_skew()
439            .await
440            .map_err(|_| Error::CircCanceled)
441    }
442}
443
444impl ServiceOnionServiceDataTunnel {
445    /// Tell this tunnel to begin allowing the final hop of the tunnel to try
446    /// to create new Tor streams, and to return those pending requests in an
447    /// asynchronous stream.
448    ///
449    /// Ordinarily, these requests are rejected.
450    ///
451    /// There can only be one [`Stream`](futures::Stream) of this type created on a given tunnel.
452    /// If a such a [`Stream`](futures::Stream) already exists, this method will return
453    /// an error.
454    ///
455    /// After this method has been called on a tunnel, the tunnel is expected
456    /// to receive requests of this type indefinitely, until it is finally closed.
457    /// If the `Stream` is dropped, the next request on this tunnel will cause it to close.
458    ///
459    /// Only onion services (and eventually) exit relays should call this
460    /// method.
461    //
462    // TODO: Someday, we might want to allow a stream request handler to be
463    // un-registered.  However, nothing in the Tor protocol requires it.
464    #[cfg(feature = "hs-service")]
465    pub async fn allow_stream_requests<'a, FILT>(
466        &self,
467        allow_commands: &'a [tor_cell::relaycell::RelayCmd],
468        hop: TargetHop,
469        filter: FILT,
470    ) -> Result<
471        impl futures::Stream<Item = tor_proto::client::stream::IncomingStream> + use<'a, FILT>,
472    >
473    where
474        FILT: tor_proto::client::stream::IncomingStreamRequestFilter,
475    {
476        self.tunnel_ref()
477            .allow_stream_requests(allow_commands, hop, filter)
478            .await
479            .map_err(|error| Error::Protocol {
480                action: "allow stream requests",
481                peer: None,
482                error,
483                unique_id: Some(self.tunnel.unique_id()),
484            })
485    }
486}
487
488#[cfg(feature = "hs-service")]
489impl ServiceOnionServiceIntroTunnel {
490    /// Return the cryptographic material used to prove knowledge of a shared
491    /// secret with with `hop`.
492    ///
493    /// See [`CircuitBinding`] for more information on how this is used.
494    ///
495    /// Return None if we have no circuit binding information for the hop, or if
496    /// the hop does not exist.
497    pub async fn binding_key(&self, hop: TargetHop) -> Result<Option<CircuitBinding>> {
498        let circ = self.circuit()?;
499        circ.binding_key(hop)
500            .await
501            .map_err(|error| Error::Protocol {
502                action: "binding key",
503                peer: None,
504                error,
505                unique_id: Some(self.tunnel.unique_id()),
506            })
507    }
508}