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_cell::chancell::CircId;
16
use tor_cell::chancell::msg::HandshakeType;
17
use tor_cell::relaycell::msg::{Extend2, Extended2};
18
use tor_cell::relaycell::{AnyRelayMsgOuter, UnparsedRelayMsg};
19
use tor_error::internal;
20

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

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

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

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

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

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

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

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

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

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

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

            
179
        // If we get here, it succeeded.  Add a new hop to the circuit.
180
24
        circ.add_hop(
181
24
            path::HopDetail::Relay(self.peer_id.clone()),
182
24
            layer.fwd,
183
24
            layer.back,
184
24
            layer.binding,
185
24
            &self.settings,
186
        )?;
187
24
        Ok(MetaCellDisposition::ConversationFinished)
188
48
    }
189
}
190

            
191
impl<H> MetaCellHandler for CircuitExtender<H>
192
where
193
    H: ClientHandshake + HandshakeAuxDataHandler,
194
    H::StateType: Send,
195
    H::KeyGen: KeyGenerator,
196
{
197
60
    fn expected_hop(&self) -> HopLocation {
198
60
        (self.unique_id.unique_id(), self.expected_hop).into()
199
60
    }
200
48
    fn handle_msg(
201
48
        &mut self,
202
48
        msg: UnparsedRelayMsg,
203
48
        circ: &mut Circuit,
204
48
    ) -> Result<MetaCellDisposition> {
205
48
        let status = self.extend_circuit(msg, circ);
206

            
207
48
        if let Some(done) = self.operation_finished.take() {
208
            // ignore it if the receiving channel went away.
209
48
            let _ = done.send(status.as_ref().map(|_| ()).map_err(Clone::clone));
210
48
            status
211
        } else {
212
            Err(Error::from(internal!(
213
                "Passed two messages to an CircuitExtender!"
214
            )))
215
        }
216
48
    }
217
}
218

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

            
244
impl HandshakeAuxDataHandler for NtorV3Client {
245
42
    fn handle_server_aux_data(
246
42
        settings: &mut HopSettings,
247
42
        data: &Vec<CircResponseExt>,
248
42
    ) -> Result<()> {
249
        // Process all extensions.
250
        // If "flowctl-cc" is not enabled, this loop will always return an error, so tell clippy
251
        // that it's okay.
252
        #[cfg_attr(not(feature = "flowctl-cc"), allow(clippy::never_loop))]
253
42
        for ext in data {
254
12
            match ext {
255
12
                CircResponseExt::CcResponse(ack_ext) => {
256
                    cfg_if::cfg_if! {
257
                        if #[cfg(feature = "flowctl-cc")] {
258
                            // Unexpected ACK extension as in if CC is disabled on our side, we would never have
259
                            // requested it. Reject and circuit must be closed.
260
12
                            if !settings.ccontrol.is_enabled() {
261
                                return Err(Error::HandshakeProto(
262
                                    "Received unexpected ntorv3 CC ack extension".into(),
263
                                ));
264
12
                            }
265
12
                            let sendme_inc = ack_ext.sendme_inc();
266
                            // Invalid increment, reject and circuit must be closed.
267
12
                            if !congestion::params::is_sendme_inc_valid(sendme_inc, &settings.ccontrol) {
268
                                return Err(Error::HandshakeProto(
269
                                    "Received invalid sendme increment in CC ntorv3 extension".into(),
270
                                ));
271
12
                            }
272
                            // Excellent, we have a negotiated sendme increment. Set it for this circuit.
273
12
                            settings
274
12
                                .ccontrol
275
12
                                .cwnd_params_mut()
276
12
                                .set_sendme_inc(sendme_inc);
277
                        } else {
278
                            let _ = ack_ext;
279
                            return Err(Error::HandshakeProto(
280
                                "Received unexpected `AckCongestionControl` ntorv3 extension".into(),
281
                            ));
282
                        }
283
                    }
284
                }
285
                // Any other extensions is not expected. Reject and circuit must be closed.
286
                _ => {
287
                    return Err(Error::HandshakeProto(
288
                        "Received unexpected ntorv3 extension".into(),
289
                    ));
290
                }
291
            }
292
        }
293
42
        Ok(())
294
42
    }
295
}
296

            
297
impl HandshakeAuxDataHandler for NtorClient {
298
30
    fn handle_server_aux_data(_settings: &mut HopSettings, _data: &()) -> Result<()> {
299
        // This handshake doesn't have any auxiliary data; nothing to do.
300
30
        Ok(())
301
30
    }
302
}
303

            
304
impl HandshakeAuxDataHandler for CreateFastClient {
305
14
    fn handle_server_aux_data(_settings: &mut HopSettings, _data: &()) -> Result<()> {
306
        // This handshake doesn't have any auxiliary data; nothing to do.
307
14
        Ok(())
308
14
    }
309
}