Skip to main content

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}