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