tor_proto/relay/reactor/forward/extend_handler.rs
1//! Handler for EXTEND2 cells.
2
3use super::{CircEvent, ExtendResult, Outbound};
4
5use crate::Error;
6use crate::circuit::UniqId;
7use crate::circuit::create::{Create2Wrap, CreateHandshakeWrap};
8use crate::peer::PeerInfo;
9use crate::relay::channel_provider::{ChannelProvider, ChannelResult, OutboundChanSender};
10use crate::relay::reactor::CircuitAccount;
11use crate::util::err::ReactorError;
12use tor_cell::chancell::{AnyChanCell, CircId};
13use tor_cell::relaycell::UnparsedRelayMsg;
14use tor_cell::relaycell::msg::{Extend2, Extended2};
15use tor_error::{internal, into_internal, warn_report};
16use tor_linkspec::decode::Strictness;
17use tor_linkspec::{HasRelayIds, OwnedChanTarget, OwnedChanTargetBuilder};
18use tor_rtcompat::{Runtime, SpawnExt as _};
19
20use futures::channel::mpsc;
21use futures::{SinkExt as _, StreamExt as _};
22use tracing::{debug, trace};
23
24use std::result::Result as StdResult;
25use std::sync::Arc;
26
27/// Helper for handling EXTEND2 cells.
28pub(super) struct ExtendRequestHandler {
29 /// An identifier for logging about this handler.
30 unique_id: UniqId,
31 /// The circuit identifier on the inbound Tor channel.
32 circ_id: CircId,
33 /// Whether we have received an EXTEND2 on this circuit.
34 ///
35 // TODO(relay): bools can be finicky.
36 // Maybe we should combine this bool and the optional
37 // outbound into a new state machine type
38 // (with states Initial -> Extending -> Extended(Outbound))?
39 // But should not do this if it turns out more convoluted than the bool-based approach.
40 have_seen_extend2: bool,
41 /// A handle to a [`ChannelProvider`], used for initiating outgoing Tor channels.
42 ///
43 /// Note: all circuit reactors of a relay need to be initialized
44 /// with the *same* underlying Tor channel provider (`ChanMgr`),
45 /// to enable the reuse of existing Tor channels where possible.
46 chan_provider: Arc<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
47 /// The identity of the inbound relay (the previous hop).
48 inbound_peer: Arc<PeerInfo>,
49 /// A stream of events to be read from the main loop of the reactor.
50 event_tx: mpsc::Sender<CircEvent>,
51 /// Memory quota account
52 memquota: CircuitAccount,
53}
54
55impl ExtendRequestHandler {
56 /// Create a new [`ExtendRequestHandler`].
57 pub(super) fn new(
58 unique_id: UniqId,
59 circ_id: CircId,
60 chan_provider: Arc<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
61 inbound_peer: Arc<PeerInfo>,
62 event_tx: mpsc::Sender<CircEvent>,
63 memquota: CircuitAccount,
64 ) -> Self {
65 Self {
66 unique_id,
67 circ_id,
68 have_seen_extend2: false,
69 chan_provider,
70 inbound_peer,
71 event_tx,
72 memquota,
73 }
74 }
75
76 /// Handle an EXTEND2 cell.
77 ///
78 /// This spawns a background task for dealing with the circuit extension,
79 /// which then reports back the result via the [`Self::event_tx`] MPSC stream.
80 /// Note that this MPSC stream is polled from the `ForwardReactor` main loop,
81 /// and each `CircEvent` is passed back to [`Forward`](super::Forward)'s
82 /// [`ForwardHandler::handle_event`](crate::circuit::reactor::forward::ForwardHandler::handle_event)
83 /// implementation for handling.
84 pub(super) fn handle_extend2<R: Runtime>(
85 &mut self,
86 runtime: &R,
87 early: bool,
88 msg: UnparsedRelayMsg,
89 ) -> StdResult<(), ReactorError> {
90 // TODO(relay): this should be allowed if the AllowNonearlyExtend consensus
91 // param is set (arti#2349)
92 if !early {
93 return Err(Error::CircProto("got EXTEND2 in a RELAY cell?!".into()).into());
94 }
95
96 // Check if we're in the right state before parsing the EXTEND2
97 if self.have_seen_extend2 {
98 return Err(Error::CircProto("got 2 EXTEND2 on the same circuit?!".into()).into());
99 }
100
101 self.have_seen_extend2 = true;
102
103 let to_bytes_err = |e| Error::from_bytes_err(e, "EXTEND2 message");
104
105 let extend2 = msg.decode::<Extend2>().map_err(to_bytes_err)?.into_msg();
106
107 let chan_target = OwnedChanTargetBuilder::from_encoded_linkspecs(
108 Strictness::Standard,
109 extend2.linkspecs(),
110 )
111 .map_err(|err| Error::LinkspecDecodeErr {
112 object: "EXTEND2",
113 err,
114 })?
115 .build()
116 .map_err(|_| {
117 // TODO: should we include the error in the circ proto error context?
118 Error::CircProto("Invalid channel target".into())
119 })?;
120
121 if chan_target.has_any_relay_id_from(&*self.inbound_peer) {
122 return Err(Error::CircProto("Cannot extend circuit to previous hop".into()).into());
123 }
124
125 // Note: we don't do any further validation on the EXTEND2 here,
126 // under the assumption it will be handled by the ChannelProvider.
127
128 let (chan_tx, chan_rx) = mpsc::unbounded();
129
130 let chan_tx = OutboundChanSender(chan_tx);
131 Arc::clone(&self.chan_provider).get_or_launch(self.unique_id, chan_target, chan_tx)?;
132
133 let mut result_tx = self.event_tx.clone();
134 let rt = runtime.clone();
135 let unique_id = self.unique_id;
136 let circ_id = self.circ_id;
137 let memquota = self.memquota.clone();
138
139 // TODO(relay): because we dispatch this the entire EXTEND2 handling to a background task,
140 // we don't really need the channel provider to send us the outcome via an MPSC channel,
141 // because get_or_launch() could simply be async (it wouldn't block the reactor,
142 // because it runs in another task). Maybe we need to rethink the ChannelProvider API?
143 runtime
144 .spawn(async move {
145 let res =
146 Self::extend_circuit(rt, unique_id, circ_id, extend2, chan_rx, memquota).await;
147
148 // Discard the error if the reactor shut down before we had
149 // a chance to complete the extend handshake
150 let _ = result_tx.send(CircEvent::ExtendResult(res)).await;
151 })
152 .map_err(into_internal!("failed to spawn extend task?!"))?;
153
154 Ok(())
155 }
156
157 /// Extend this circuit on the channel received on `chan_rx`.
158 ///
159 /// Note: this gets spawned in a background task from
160 /// [`Self::handle_extend2`] so as not to block the reactor main loop.
161 async fn extend_circuit<R: Runtime>(
162 _runtime: R,
163 unique_id: UniqId,
164 inbound_circ_id: CircId,
165 extend2: Extend2,
166 mut chan_rx: mpsc::UnboundedReceiver<ChannelResult>,
167 memquota: CircuitAccount,
168 ) -> StdResult<ExtendResult, ReactorError> {
169 // We expect the channel build timeout to be enforced by the ChannelProvider
170 let chan_res = chan_rx
171 .next()
172 .await
173 .ok_or_else(|| internal!("channel provider task exited"))?;
174
175 let channel = match chan_res {
176 Ok(c) => c,
177 Err(e) => {
178 warn_report!(e, "Failed to launch outgoing channel");
179 // Note: retries are handled within
180 // get_or_launch(), so if we receive an
181 // error at this point, we need to bail
182 return Err(ReactorError::Shutdown);
183 }
184 };
185
186 debug!(
187 circ_uniq_id = %unique_id,
188 backward_circ_id = %inbound_circ_id,
189 "Launched channel to the next hop"
190 );
191
192 // Now that we finally have a forward Tor channel,
193 // it's time to forward the onion skin and extend the circuit...
194 //
195 // Note: the only reason we need to await here is because internally
196 // new_outbound_circ() sends a control message to the channel reactor handles,
197 // which is handled asynchronously. In practice, we're not actually waiting on
198 // the network here, so in theory we shouldn't need a timeout for this operation.
199 let (circ_id, outbound_chan_rx, createdreceiver) =
200 channel.new_outbound_circ(memquota).await?;
201
202 // We have allocated a circuit in the channel's circmap,
203 // now it's time to send the CREATE2 and wait for the response.
204 let create2_wrap = Create2Wrap {
205 handshake_type: extend2.handshake_type(),
206 };
207 let create2 = create2_wrap.to_chanmsg(extend2.handshake().into());
208
209 // Time to write the CREATE2 to the outbound channel...
210 let mut outbound_chan_tx = channel.sender();
211 let cell = AnyChanCell::new(Some(circ_id), create2);
212
213 trace!(
214 circ_uniq_id = %unique_id,
215 forward_circ_id = %circ_id,
216 "Sending CREATE2 to the next hop"
217 );
218
219 outbound_chan_tx.send((cell, None)).await?;
220
221 // TODO(relay): we need a timeout here, otherwise we might end up waiting forever
222 // for the CREATED2 to arrive.
223 //
224 // There is some complexity here, see
225 // https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/3648#note_3340125
226 let response = createdreceiver
227 .await
228 .map_err(|_| internal!("channel disappeared?"))?;
229
230 trace!(
231 circ_uniq_id = %unique_id,
232 forward_circ_id = %circ_id,
233 "Got CREATED2 response from next hop"
234 );
235
236 let outbound = Outbound {
237 circ_id,
238 channel: Arc::clone(&channel),
239 outbound_chan_tx,
240 };
241
242 // If we reach this point, it means we have extended
243 // the circuit by one hop, so we need to take the contents
244 // of the CREATE/CREATED2 cell, and package an EXTEND/EXTENDED2
245 // to send back to the client.
246 let created2_body = create2_wrap.decode_chanmsg(response)?;
247 let extended2 = Extended2::new(created2_body);
248
249 Ok(ExtendResult {
250 extended2,
251 outbound,
252 outbound_chan_rx,
253 })
254 }
255}