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, ¶ms, 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}