1
//! Handler for EXTEND2 cells.
2

            
3
use super::{CircEvent, ExtendResult, Outbound};
4

            
5
use crate::Error;
6
use crate::circuit::UniqId;
7
use crate::circuit::create::{Create2Wrap, CreateHandshakeWrap};
8
use crate::peer::PeerInfo;
9
use crate::relay::channel_provider::{ChannelProvider, ChannelResult, OutboundChanSender};
10
use crate::relay::reactor::CircuitAccount;
11
use crate::util::err::ReactorError;
12
use tor_cell::chancell::{AnyChanCell, CircId};
13
use tor_cell::relaycell::UnparsedRelayMsg;
14
use tor_cell::relaycell::msg::{Extend2, Extended2};
15
use tor_error::{internal, into_internal, warn_report};
16
use tor_linkspec::decode::Strictness;
17
use tor_linkspec::{HasRelayIds, OwnedChanTarget, OwnedChanTargetBuilder};
18
use tor_rtcompat::{Runtime, SpawnExt as _};
19

            
20
use futures::channel::mpsc;
21
use futures::{SinkExt as _, StreamExt as _};
22
use tracing::{debug, trace};
23

            
24
use std::result::Result as StdResult;
25
use std::sync::Arc;
26

            
27
/// Helper for handling EXTEND2 cells.
28
pub(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

            
55
impl ExtendRequestHandler {
56
    /// Create a new [`ExtendRequestHandler`].
57
58
    pub(super) fn new(
58
58
        unique_id: UniqId,
59
58
        circ_id: CircId,
60
58
        chan_provider: Arc<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
61
58
        inbound_peer: Arc<PeerInfo>,
62
58
        event_tx: mpsc::Sender<CircEvent>,
63
58
        memquota: CircuitAccount,
64
58
    ) -> Self {
65
58
        Self {
66
58
            unique_id,
67
58
            circ_id,
68
58
            have_seen_extend2: false,
69
58
            chan_provider,
70
58
            inbound_peer,
71
58
            event_tx,
72
58
            memquota,
73
58
        }
74
58
    }
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
20
    pub(super) fn handle_extend2<R: Runtime>(
85
20
        &mut self,
86
20
        runtime: &R,
87
20
        early: bool,
88
20
        msg: UnparsedRelayMsg,
89
20
    ) -> StdResult<(), ReactorError> {
90
        // TODO(relay): this should be allowed if the AllowNonearlyExtend consensus
91
        // param is set (arti#2349)
92
20
        if !early {
93
4
            return Err(Error::CircProto("got EXTEND2 in a RELAY cell?!".into()).into());
94
16
        }
95

            
96
        // Check if we're in the right state before parsing the EXTEND2
97
16
        if self.have_seen_extend2 {
98
            return Err(Error::CircProto("got 2 EXTEND2 on the same circuit?!".into()).into());
99
16
        }
100

            
101
16
        self.have_seen_extend2 = true;
102

            
103
16
        let to_bytes_err = |e| Error::from_bytes_err(e, "EXTEND2 message");
104

            
105
16
        let extend2 = msg.decode::<Extend2>().map_err(to_bytes_err)?.into_msg();
106

            
107
16
        let chan_target = OwnedChanTargetBuilder::from_encoded_linkspecs(
108
16
            Strictness::Standard,
109
16
            extend2.linkspecs(),
110
        )
111
16
        .map_err(|err| Error::LinkspecDecodeErr {
112
            object: "EXTEND2",
113
            err,
114
        })?
115
16
        .build()
116
16
        .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
16
        if chan_target.has_any_relay_id_from(&*self.inbound_peer) {
122
4
            return Err(Error::CircProto("Cannot extend circuit to previous hop".into()).into());
123
12
        }
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
12
        let (chan_tx, chan_rx) = mpsc::unbounded();
129

            
130
12
        let chan_tx = OutboundChanSender(chan_tx);
131
12
        Arc::clone(&self.chan_provider).get_or_launch(self.unique_id, chan_target, chan_tx)?;
132

            
133
12
        let mut result_tx = self.event_tx.clone();
134
12
        let rt = runtime.clone();
135
12
        let unique_id = self.unique_id;
136
12
        let circ_id = self.circ_id;
137
12
        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
12
        runtime
144
12
            .spawn(async move {
145
12
                let res =
146
12
                    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
12
                let _ = result_tx.send(CircEvent::ExtendResult(res)).await;
151
12
            })
152
12
            .map_err(into_internal!("failed to spawn extend task?!"))?;
153

            
154
12
        Ok(())
155
20
    }
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
12
    async fn extend_circuit<R: Runtime>(
162
12
        _runtime: R,
163
12
        unique_id: UniqId,
164
12
        inbound_circ_id: CircId,
165
12
        extend2: Extend2,
166
12
        mut chan_rx: mpsc::UnboundedReceiver<ChannelResult>,
167
12
        memquota: CircuitAccount,
168
12
    ) -> StdResult<ExtendResult, ReactorError> {
169
        // We expect the channel build timeout to be enforced by the ChannelProvider
170
12
        let chan_res = chan_rx
171
12
            .next()
172
12
            .await
173
12
            .ok_or_else(|| internal!("channel provider task exited"))?;
174

            
175
12
        let channel = match chan_res {
176
12
            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
12
        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
12
        let (circ_id, outbound_chan_rx, createdreceiver) =
200
12
            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
12
        let create2_wrap = Create2Wrap {
205
12
            handshake_type: extend2.handshake_type(),
206
12
        };
207
12
        let create2 = create2_wrap.to_chanmsg(extend2.handshake().into());
208

            
209
        // Time to write the CREATE2 to the outbound channel...
210
12
        let mut outbound_chan_tx = channel.sender();
211
12
        let cell = AnyChanCell::new(Some(circ_id), create2);
212

            
213
12
        trace!(
214
            circ_uniq_id = %unique_id,
215
            forward_circ_id = %circ_id,
216
            "Sending CREATE2 to the next hop"
217
        );
218

            
219
12
        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
12
        let response = createdreceiver
227
12
            .await
228
12
            .map_err(|_| internal!("channel disappeared?"))?;
229

            
230
12
        trace!(
231
            circ_uniq_id = %unique_id,
232
            forward_circ_id = %circ_id,
233
            "Got CREATED2 response from next hop"
234
        );
235

            
236
12
        let outbound = Outbound {
237
12
            circ_id,
238
12
            channel: Arc::clone(&channel),
239
12
            outbound_chan_tx,
240
12
        };
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
12
        let created2_body = create2_wrap.decode_chanmsg(response)?;
247
12
        let extended2 = Extended2::new(created2_body);
248

            
249
12
        Ok(ExtendResult {
250
12
            extended2,
251
12
            outbound,
252
12
            outbound_chan_rx,
253
12
        })
254
12
    }
255
}