1
//! Module providing [`CircuitExtender`].
2

            
3
use super::{Circuit, ReactorResultChannel};
4
use crate::circuit::circhop::HopSettings;
5
use crate::client::circuit::handshake::HandshakeRole;
6
use crate::client::reactor::MetaCellDisposition;
7
use crate::crypto::cell::HopNum;
8
use crate::crypto::handshake::fast::CreateFastClient;
9
use crate::crypto::handshake::ntor_v3::NtorV3Client;
10
use crate::tunnel::TunnelScopedCircId;
11
use crate::{Error, Result};
12
use crate::{HopLocation, congestion};
13
use oneshot_fused_workaround as oneshot;
14
use std::borrow::Borrow;
15
use tor_basic_utils::onionperf_types::{OnionperfCircuitStatus, OnionperfEvent};
16
use tor_cell::chancell::CircId;
17
use tor_cell::chancell::msg::HandshakeType;
18
use tor_cell::relaycell::msg::{Extend2, Extended2};
19
use tor_cell::relaycell::{AnyRelayMsgOuter, UnparsedRelayMsg};
20
use tor_error::internal;
21

            
22
use crate::circuit::circhop::SendRelayCell;
23
use crate::client::circuit::path;
24
use crate::client::reactor::MetaCellHandler;
25
use crate::crypto::handshake::ntor::NtorClient;
26
use crate::crypto::handshake::{ClientHandshake, KeyGenerator};
27
use tor_cell::relaycell::extend::CircResponseExt;
28
use tor_linkspec::{EncodedLinkSpec, OwnedChanTarget};
29
use tracing::trace;
30

            
31
/// An object that can extend a circuit by one hop, using the `MetaCellHandler` trait.
32
///
33
/// Yes, I know having trait bounds on structs is bad, but in this case it's necessary
34
/// since we want to be able to use `H::KeyType`.
35
pub(crate) struct CircuitExtender<H>
36
where
37
    H: ClientHandshake,
38
{
39
    /// The peer that we're extending to.
40
    ///
41
    /// Used to extend our record of the circuit's path.
42
    peer_id: OwnedChanTarget,
43
    /// Handshake state.
44
    state: Option<H::StateType>,
45
    /// In-progress settings that we're negotiating for this hop.
46
    settings: HopSettings,
47
    /// An identifier for logging about this reactor's circuit.
48
    unique_id: TunnelScopedCircId,
49
    /// The circuit identifier on the channel.
50
    circ_id: CircId,
51
    /// The hop we're expecting the EXTENDED2 cell to come back from.
52
    expected_hop: HopNum,
53
    /// A oneshot channel that we should inform when we are done with this extend operation.
54
    operation_finished: Option<oneshot::Sender<Result<()>>>,
55
}
56
impl<H> CircuitExtender<H>
57
where
58
    H: ClientHandshake + HandshakeAuxDataHandler,
59
    H::KeyGen: KeyGenerator,
60
{
61
    /// Start extending a circuit, sending the necessary EXTEND cell and returning a
62
    /// new `CircuitExtender` to be called when the reply arrives.
63
    ///
64
    /// The `handshake_id` is the numeric identifier for what kind of
65
    /// handshake we're doing.  The `key` is the relay's onion key that
66
    /// goes along with the handshake, and the `linkspecs` are the
67
    /// link specifiers to include in the EXTEND cell to tell the
68
    /// current last hop which relay to connect to.
69
    #[allow(clippy::too_many_arguments)]
70
    #[allow(clippy::blocks_in_conditions)]
71
72
    pub(crate) fn begin(
72
72
        peer_id: OwnedChanTarget,
73
72
        handshake_id: HandshakeType,
74
72
        key: &H::KeyType,
75
72
        linkspecs: Vec<EncodedLinkSpec>,
76
72
        settings: HopSettings,
77
72
        client_aux_data: &impl Borrow<H::ClientAuxData>,
78
72
        circ: &mut Circuit,
79
72
        done: ReactorResultChannel<()>,
80
72
    ) -> Result<(Self, SendRelayCell)> {
81
72
        match (|| {
82
72
            let mut rng = rand::rng();
83
72
            let unique_id = circ.unique_id;
84
72
            let circ_id = circ.circ_id;
85

            
86
72
            let (state, msg) = H::client1(&mut rng, key, client_aux_data)?;
87
72
            let n_hops = circ.crypto_out.n_layers();
88
72
            let hop = ((n_hops - 1) as u8).into();
89
72
            trace!(
90
                circ_uniq_id = %unique_id,
91
                forward_circ_id = %circ_id,
92
                target_hop = n_hops + 1,
93
                linkspecs = ?linkspecs,
94
                "Extending circuit",
95
            );
96
72
            let extend_msg = Extend2::new(linkspecs, handshake_id, msg);
97
72
            let cell = AnyRelayMsgOuter::new(None, extend_msg.into());
98
            // Prepare a message to send message to the last hop...
99
72
            let cell = SendRelayCell {
100
72
                hop: Some(hop),
101
72
                early: true, // use a RELAY_EARLY cel
102
72
                cell,
103
72
            };
104

            
105
72
            trace!(
106
                circ_uniq_id = %unique_id,
107
                forward_circ_id = %circ_id,
108
                "waiting for EXTENDED2 cell"
109
            );
110
            // ... and now we wait for a response.
111
72
            let extender = Self {
112
72
                peer_id,
113
72
                state: Some(state),
114
72
                settings,
115
72
                unique_id,
116
72
                circ_id,
117
72
                expected_hop: hop,
118
72
                operation_finished: None,
119
72
            };
120

            
121
72
            Ok::<(CircuitExtender<_>, SendRelayCell), Error>((extender, cell))
122
        })() {
123
72
            Ok(mut result) => {
124
72
                result.0.operation_finished = Some(done);
125
72
                Ok(result)
126
            }
127
            Err(e) => {
128
                // It's okay if the receiver went away.
129
                let _ = done.send(Err(e.clone()));
130
                Err(e)
131
            }
132
        }
133
72
    }
134

            
135
    /// Perform the work of extending the circuit another hop.
136
    ///
137
    /// This is a separate function to simplify the error-handling work of handle_msg().
138
48
    fn extend_circuit(
139
48
        &mut self,
140
48
        msg: UnparsedRelayMsg,
141
48
        circ: &mut Circuit,
142
48
    ) -> Result<MetaCellDisposition> {
143
48
        let msg = msg
144
48
            .decode::<Extended2>()
145
48
            .map_err(|e| Error::from_bytes_err(e, "extended2 message"))?
146
36
            .into_msg();
147

            
148
36
        let relay_handshake = msg.into_body();
149

            
150
36
        trace!(
151
            circ_uniq_id = %self.unique_id,
152
            forward_circ_id = %self.circ_id,
153
            "Received EXTENDED2 cell; completing handshake.",
154
        );
155
        // Now perform the second part of the handshake, and see if it
156
        // succeeded.
157
36
        let (server_aux_data, keygen) = H::client2(
158
36
            self.state
159
36
                .take()
160
36
                .expect("CircuitExtender::finish() called twice"),
161
36
            relay_handshake,
162
12
        )?;
163

            
164
        // Handle auxiliary data returned from the server, e.g. validating that
165
        // requested extensions have been acknowledged.
166
24
        H::handle_server_aux_data(&mut self.settings, &server_aux_data)?;
167

            
168
24
        let layer = self
169
24
            .settings
170
24
            .relay_crypt_protocol()
171
24
            .construct_client_layers(HandshakeRole::Initiator, keygen)?;
172

            
173
24
        trace!(
174
            circ_uniq_id = %self.unique_id,
175
            forward_circ_id = %self.circ_id,
176
            settings = ?self.settings,
177
            "Handshake complete; circuit extended."
178
        );
179

            
180
24
        trace!(
181
            onionperf = true,
182
            circ_uniq_id = %self.unique_id,
183
            forward_circ_id = %self.circ_id,
184
            event = ?OnionperfEvent::Circuit(OnionperfCircuitStatus::Extended),
185
        );
186

            
187
        // If we get here, it succeeded.  Add a new hop to the circuit.
188
24
        circ.add_hop(
189
24
            path::HopDetail::Relay(self.peer_id.clone()),
190
24
            layer.fwd,
191
24
            layer.back,
192
24
            layer.binding,
193
24
            &self.settings,
194
        )?;
195
24
        Ok(MetaCellDisposition::ConversationFinished)
196
48
    }
197
}
198

            
199
impl<H> MetaCellHandler for CircuitExtender<H>
200
where
201
    H: ClientHandshake + HandshakeAuxDataHandler,
202
    H::StateType: Send,
203
    H::KeyGen: KeyGenerator,
204
{
205
60
    fn expected_hop(&self) -> HopLocation {
206
60
        (self.unique_id.unique_id(), self.expected_hop).into()
207
60
    }
208
48
    fn handle_msg(
209
48
        &mut self,
210
48
        msg: UnparsedRelayMsg,
211
48
        circ: &mut Circuit,
212
48
    ) -> Result<MetaCellDisposition> {
213
48
        let status = self.extend_circuit(msg, circ);
214

            
215
48
        if let Some(done) = self.operation_finished.take() {
216
            // ignore it if the receiving channel went away.
217
48
            let _ = done.send(status.as_ref().map(|_| ()).map_err(Clone::clone));
218
48
            status
219
        } else {
220
            Err(Error::from(internal!(
221
                "Passed two messages to an CircuitExtender!"
222
            )))
223
        }
224
48
    }
225
}
226

            
227
/// Specifies handling of auxiliary handshake data for a given `ClientHandshake`.
228
//
229
// For simplicity we implement this as a trait of the handshake object itself.
230
// This is currently sufficient because
231
//
232
// 1. We only need or want one handler implementation for a given handshake type.
233
// 2. We currently don't need to keep extra state; i.e. its method doesn't take
234
//    &self.
235
//
236
// If we end up wanting to instantiate objects for one or both of the
237
// `ClientHandshake` object or the `HandshakeAuxDataHandler` object, we could
238
// decouple them by making this something like:
239
//
240
// ```
241
// trait HandshakeAuxDataHandler<H> where H: ClientHandshake
242
// ```
243
pub(crate) trait HandshakeAuxDataHandler: ClientHandshake {
244
    /// Handle auxiliary handshake data returned when creating or extending a
245
    /// circuit.
246
    fn handle_server_aux_data(
247
        settings: &mut HopSettings,
248
        data: &<Self as ClientHandshake>::ServerAuxData,
249
    ) -> Result<()>;
250
}
251

            
252
impl HandshakeAuxDataHandler for NtorV3Client {
253
42
    fn handle_server_aux_data(
254
42
        settings: &mut HopSettings,
255
42
        data: &Vec<CircResponseExt>,
256
42
    ) -> Result<()> {
257
        // Did we get a `CcResponse` extension?
258
42
        let mut cc_response = false;
259

            
260
        // Process all extensions.
261
42
        for ext in data {
262
12
            match ext {
263
12
                CircResponseExt::CcResponse(ack_ext) => {
264
12
                    cc_response = true;
265

            
266
                    // Unexpected ACK extension as in if CC is disabled on our side, we would never have
267
                    // requested it. Reject and circuit must be closed.
268
12
                    if !settings.ccontrol.is_enabled() {
269
                        return Err(Error::HandshakeProto(
270
                            "Received unexpected ntorv3 CC ack extension".into(),
271
                        ));
272
12
                    }
273
12
                    let sendme_inc = ack_ext.sendme_inc();
274
                    // Invalid increment, reject and circuit must be closed.
275
12
                    if !congestion::params::is_sendme_inc_valid(sendme_inc, &settings.ccontrol) {
276
                        return Err(Error::HandshakeProto(
277
                            "Received invalid sendme increment in CC ntorv3 extension".into(),
278
                        ));
279
12
                    }
280
                    // Excellent, we have a negotiated sendme increment. Set it for this circuit.
281
12
                    settings
282
12
                        .ccontrol
283
12
                        .cwnd_params_mut()
284
12
                        .set_sendme_inc(sendme_inc);
285
                }
286
                // Any other extensions is not expected. Reject and circuit must be closed.
287
                _ => {
288
                    return Err(Error::HandshakeProto(
289
                        "Received unexpected ntorv3 extension".into(),
290
                    ));
291
                }
292
            }
293
        }
294

            
295
        // If we requested congestion control but did not receive a congestion control response.
296
42
        if settings.ccontrol.is_enabled() && !cc_response {
297
            // The exact behaviour here isn't yet decided:
298
            // https://gitlab.torproject.org/tpo/core/arti/-/work_items/2670
299
            //
300
            // But regardless we need to do something since the client and relay are not in
301
            // agreement about the circuit options, and the circuit will not work properly.
302
            // So for now we just close the circuit.
303
            return Err(Error::HandshakeProto(
304
                "Requested congestion control but did not receive an ntor-v3 cc response".into(),
305
            ));
306
42
        }
307

            
308
42
        Ok(())
309
42
    }
310
}
311

            
312
impl HandshakeAuxDataHandler for NtorClient {
313
30
    fn handle_server_aux_data(_settings: &mut HopSettings, _data: &()) -> Result<()> {
314
        // This handshake doesn't have any auxiliary data; nothing to do.
315
30
        Ok(())
316
30
    }
317
}
318

            
319
impl HandshakeAuxDataHandler for CreateFastClient {
320
14
    fn handle_server_aux_data(_settings: &mut HopSettings, _data: &()) -> Result<()> {
321
        // This handshake doesn't have any auxiliary data; nothing to do.
322
14
        Ok(())
323
14
    }
324
}