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