1
//! Incoming data stream cell handlers, shared by the relay and onion service implementations.
2

            
3
use bitvec::prelude::*;
4
use derive_deftly::Deftly;
5
use oneshot_fused_workaround as oneshot;
6

            
7
use tor_cell::relaycell::{RelayCellFormat, RelayCmd, StreamId, UnparsedRelayMsg, msg};
8
use tor_cell::restricted_msg;
9
use tor_error::internal;
10
use tor_memquota::derive_deftly_template_HasMemoryCost;
11
use tor_memquota::mq_queue::{self, MpscSpec};
12
use tor_rtcompat::DynTimeProvider;
13

            
14
use crate::circuit::CircHopSyncView;
15
use crate::circuit::circhop::ReactorStreamComponents;
16
use crate::stream::cmdcheck::{AnyCmdChecker, CmdChecker, StreamStatus};
17
use crate::stream::{CloseStreamBehavior, StreamComponents};
18
use crate::{Error, Result};
19

            
20
// TODO(relay): move these to a shared module
21
use crate::client::stream::DataStream;
22

            
23
use crate::memquota::StreamAccount;
24
use crate::{HopLocation, HopNum};
25

            
26
/// A `CmdChecker` that enforces invariants for inbound data streams.
27
#[derive(Debug, Default)]
28
pub(crate) struct InboundDataCmdChecker;
29

            
30
restricted_msg! {
31
    /// An allowable incoming message on an incoming data stream.
32
    enum IncomingDataStreamMsg:RelayMsg {
33
        // SENDME is handled by the reactor.
34
        Data, End,
35
    }
36
}
37

            
38
impl CmdChecker for InboundDataCmdChecker {
39
28
    fn check_msg(&mut self, msg: &tor_cell::relaycell::UnparsedRelayMsg) -> Result<StreamStatus> {
40
        use StreamStatus::*;
41
28
        match msg.cmd() {
42
28
            RelayCmd::DATA => Ok(Open),
43
            RelayCmd::END => Ok(Closed),
44
            _ => Err(Error::StreamProto(format!(
45
                "Unexpected {} on an incoming data stream!",
46
                msg.cmd()
47
            ))),
48
        }
49
28
    }
50

            
51
    fn consume_checked_msg(&mut self, msg: tor_cell::relaycell::UnparsedRelayMsg) -> Result<()> {
52
        let _ = msg
53
            .decode::<IncomingDataStreamMsg>()
54
            .map_err(|err| Error::from_bytes_err(err, "cell on half-closed stream"))?;
55
        Ok(())
56
    }
57
}
58

            
59
impl InboundDataCmdChecker {
60
    /// Return a new boxed `DataCmdChecker` in a state suitable for a
61
    /// connection where an initial CONNECTED cell is not expected.
62
    ///
63
    /// This is used by hidden services, exit relays, and directory servers
64
    /// to accept streams.
65
52
    pub(crate) fn new_connected() -> AnyCmdChecker {
66
52
        Box::new(Self)
67
52
    }
68
}
69

            
70
/// A pending request from the other end of the circuit for us to open a new
71
/// stream.
72
///
73
/// Exits, directory caches, and onion services expect to receive these; others
74
/// do not.
75
///
76
/// On receiving one of these objects, the party handling it should accept it or
77
/// reject it.  If it is dropped without being explicitly handled, a reject
78
/// message will be sent anyway.
79
#[derive(Debug)]
80
pub struct IncomingStream {
81
    /// The runtime's time provider.
82
    time_provider: DynTimeProvider,
83
    /// The message that the client sent us to begin the stream.
84
    request: IncomingStreamRequest,
85
    /// Stream components used to assemble the [`DataStream`].
86
    components: StreamComponents,
87
}
88

            
89
impl IncomingStream {
90
    /// Create a new `IncomingStream`.
91
52
    pub(crate) fn new(
92
52
        time_provider: DynTimeProvider,
93
52
        request: IncomingStreamRequest,
94
52
        components: StreamComponents,
95
52
    ) -> Self {
96
52
        Self {
97
52
            time_provider,
98
52
            request,
99
52
            components,
100
52
        }
101
52
    }
102

            
103
    /// Return the underlying message that was used to try to begin this stream.
104
4
    pub fn request(&self) -> &IncomingStreamRequest {
105
4
        &self.request
106
4
    }
107

            
108
    /// Accept this stream as a new [`DataStream`], and send the client a
109
    /// message letting them know the stream was accepted.
110
    ///
111
    /// Returns an error if this is not a data or directory stream.
112
42
    pub async fn accept_data(self, message: msg::Connected) -> Result<DataStream> {
113
        let Self {
114
28
            time_provider,
115
28
            request,
116
            components:
117
                StreamComponents {
118
28
                    mut target,
119
28
                    stream_receiver,
120
28
                    xon_xoff_reader_ctrl,
121
28
                    memquota,
122
                },
123
28
        } = self;
124

            
125
28
        match request {
126
            IncomingStreamRequest::Begin(_) | IncomingStreamRequest::BeginDir(_) => {
127
28
                target.send(message.into()).await?;
128
28
                Ok(DataStream::new_connected(
129
28
                    time_provider,
130
28
                    stream_receiver,
131
28
                    xon_xoff_reader_ctrl,
132
28
                    target,
133
28
                    memquota,
134
28
                ))
135
            }
136
            IncomingStreamRequest::Resolve(_) => {
137
                Err(internal!("Cannot accept data on a RESOLVE stream").into())
138
            }
139
        }
140
28
    }
141

            
142
    /// Respond to a DNS stream by sending the specified `message` to the client.
143
    ///
144
    /// This closes the stream.
145
    ///
146
    /// Returns an error this is not a DNS stream.
147
    #[cfg(feature = "relay")]
148
6
    pub async fn resolve(mut self, message: msg::Resolved) -> Result<()> {
149
4
        match self.request {
150
            IncomingStreamRequest::Begin(_) | IncomingStreamRequest::BeginDir(_) => {
151
                Err(internal!("Cannot send RESOLVED on a data or directory stream").into())
152
            }
153
            IncomingStreamRequest::Resolve(_) => {
154
4
                let rx = self.close(CloseStreamBehavior::SendResolved(message))?;
155

            
156
4
                rx.await.map_err(|_| Error::CircuitClosed)?
157
            }
158
        }
159
4
    }
160

            
161
    /// Reject this request and send an error message to the client.
162
24
    pub async fn reject(mut self, message: msg::End) -> Result<()> {
163
16
        let rx = self.close(CloseStreamBehavior::SendEnd(message))?;
164

            
165
16
        rx.await.map_err(|_| Error::CircuitClosed)?
166
16
    }
167

            
168
    /// Close this stream, and possibly send a message (END or RESOLVED) to the client.
169
    ///
170
    /// Returns a [`oneshot::Receiver`] that can be used to await the reactor's response.
171
20
    fn close(&mut self, message: CloseStreamBehavior) -> Result<oneshot::Receiver<Result<()>>> {
172
20
        self.components.target.close_pending(message)
173
20
    }
174

            
175
    /// Ignore this request without replying to the client.
176
    ///
177
    /// (If you drop an [`IncomingStream`] without calling `accept_data`,
178
    /// `reject`, or this method, the drop handler will cause it to be
179
    /// rejected.)
180
    pub async fn discard(mut self) -> Result<()> {
181
        let rx = self.close(CloseStreamBehavior::SendNothing)?;
182

            
183
        rx.await.map_err(|_| Error::CircuitClosed)?.map(|_| ())
184
    }
185
}
186

            
187
// NOTE: We do not need to `impl Drop for IncomingStream { .. }`: when its
188
// StreamTarget is dropped, this will drop its internal mpsc::Sender, and the
189
// circuit reactor will see a close on its mpsc::Receiver, and the circuit
190
// reactor will itself send an End.
191

            
192
restricted_msg! {
193
    /// The allowed incoming messages on an `IncomingStream`.
194
    #[derive(Clone, Debug, Deftly)]
195
    #[derive_deftly(HasMemoryCost)]
196
    #[non_exhaustive]
197
    pub enum IncomingStreamRequest: RelayMsg {
198
        /// A BEGIN message.
199
        Begin,
200
        /// A BEGIN_DIR message.
201
        BeginDir,
202
        /// A RESOLVE message.
203
        Resolve,
204
    }
205
}
206

            
207
/// Bit-vector used to represent a list of permitted commands.
208
///
209
/// This is cheaper and faster than using a vec, and avoids side-channel
210
/// attacks.
211
type RelayCmdSet = bitvec::BitArr!(for 256);
212

            
213
/// A `CmdChecker` that enforces correctness for incoming commands on unrecognized streams that
214
/// have a non-zero stream ID.
215
#[derive(Debug)]
216
pub(crate) struct IncomingCmdChecker {
217
    /// The "begin" commands that can be received on this type of circuit:
218
    ///
219
    ///   * onion service circuits only accept `BEGIN`
220
    ///   * all relay circuits accept `BEGIN_DIR`
221
    ///   * exit relays additionally accept `BEGIN` or `RESOLVE` on relay circuits
222
    ///   * once CONNECT_UDP is implemented, relays and later onion services may accept CONNECT_UDP
223
    ///     as well
224
    allow_commands: RelayCmdSet,
225
}
226

            
227
impl IncomingCmdChecker {
228
    /// Create a new boxed `IncomingCmdChecker`.
229
130
    pub(crate) fn new_any(allow_commands: &[RelayCmd]) -> AnyCmdChecker {
230
130
        let mut array = BitArray::ZERO;
231
130
        for c in allow_commands {
232
116
            array.set(u8::from(*c) as usize, true);
233
116
        }
234
130
        Box::new(Self {
235
130
            allow_commands: array,
236
130
        })
237
130
    }
238
}
239

            
240
impl CmdChecker for IncomingCmdChecker {
241
80
    fn check_msg(&mut self, msg: &UnparsedRelayMsg) -> Result<StreamStatus> {
242
80
        if self.allow_commands[u8::from(msg.cmd()) as usize] {
243
60
            Ok(StreamStatus::Open)
244
        } else {
245
20
            Err(Error::StreamProto(format!(
246
20
                "Unexpected {} on incoming stream",
247
20
                msg.cmd()
248
20
            )))
249
        }
250
80
    }
251

            
252
    fn consume_checked_msg(&mut self, msg: UnparsedRelayMsg) -> Result<()> {
253
        let _ = msg
254
            .decode::<IncomingStreamRequest>()
255
            .map_err(|err| Error::from_bytes_err(err, "invalid message on incoming stream"))?;
256

            
257
        Ok(())
258
    }
259
}
260

            
261
/// A callback that can check whether a given stream request is acceptable
262
/// immediately on its receipt.
263
///
264
/// This should only be used for checks that need to be done immediately, with a
265
/// view of the state of the circuit hop the stream request arrived on.
266
/// Any other checks should, if possible,
267
/// be done on the [`IncomingStream`] objects as they are received.
268
pub trait IncomingStreamRequestFilter: Send + 'static {
269
    /// Check an incoming stream request, and decide what to do with it.
270
    fn disposition(
271
        &mut self,
272
        ctx: &IncomingStreamRequestContext<'_>,
273
        circ: &CircHopSyncView<'_>,
274
    ) -> Result<IncomingStreamRequestDisposition>;
275
}
276

            
277
/// What action to take with an incoming stream request.
278
#[derive(Clone, Debug)]
279
#[non_exhaustive]
280
pub enum IncomingStreamRequestDisposition {
281
    /// Accept the request (for now) and pass it to the mpsc::Receiver
282
    /// that is yielding them as [`IncomingStream``
283
    Accept,
284
    /// Rejected the request, and close the circuit on which it was received.
285
    CloseCircuit,
286
    /// Reject the request and send an END message.
287
    RejectRequest(msg::End),
288
}
289

            
290
/// Information about a stream request, as passed to an [`IncomingStreamRequestFilter`].
291
pub struct IncomingStreamRequestContext<'a> {
292
    /// The request message itself
293
    pub(crate) request: &'a IncomingStreamRequest,
294
}
295
impl<'a> IncomingStreamRequestContext<'a> {
296
    /// Return a reference to the message used to request this stream.
297
    pub fn request(&self) -> &'a IncomingStreamRequest {
298
        self.request
299
    }
300
}
301

            
302
/// A no-op request filter to be used in testing.
303
#[cfg(test)]
304
#[derive(Copy, Clone, Debug, Default)]
305
pub(crate) struct NoOpRequestFilter;
306

            
307
#[cfg(test)]
308
impl IncomingStreamRequestFilter for NoOpRequestFilter {
309
    fn disposition(
310
        &mut self,
311
        _ctx: &IncomingStreamRequestContext<'_>,
312
        _circ: &CircHopSyncView<'_>,
313
    ) -> crate::Result<IncomingStreamRequestDisposition> {
314
        Ok(IncomingStreamRequestDisposition::Accept)
315
    }
316
}
317

            
318
/// Information about an incoming stream request.
319
#[derive(Debug, Deftly)]
320
#[derive_deftly(HasMemoryCost)]
321
pub(crate) struct StreamReqInfo {
322
    /// The [`IncomingStreamRequest`].
323
    pub(crate) req: IncomingStreamRequest,
324
    /// The ID of the stream being requested.
325
    pub(crate) stream_id: StreamId,
326
    /// The [`HopNum`].
327
    ///
328
    /// Set to `None` if we are an exit relay.
329
    //
330
    // TODO: For onion services, we might be able to enforce the HopNum earlier: we would never accept an
331
    // incoming stream request from two separate hops.  (There is only one that's valid.)
332
    pub(crate) hop: Option<HopLocation>,
333
    /// The format which must be used with this stream to encode messages.
334
    #[deftly(has_memory_cost(indirect_size = "0"))]
335
    pub(crate) relay_cell_format: RelayCellFormat,
336
    /// A collection of queues/channels that can be used to interact with this stream.
337
    pub(crate) stream_components: ReactorStreamComponents,
338
    /// The memory quota account to be used for this stream
339
    #[deftly(has_memory_cost(indirect_size = "0"))] // estimate (it contains an Arc)
340
    pub(crate) memquota: StreamAccount,
341
}
342

            
343
/// MPSC queue containing stream requests
344
#[cfg(any(feature = "hs-service", feature = "relay"))]
345
pub(crate) type StreamReqSender = mq_queue::Sender<StreamReqInfo, MpscSpec>;
346

            
347
/// Data required for handling an incoming stream request.
348
#[derive(educe::Educe)]
349
#[educe(Debug)]
350
#[cfg(any(feature = "hs-service", feature = "relay"))]
351
pub(crate) struct IncomingStreamRequestHandler {
352
    /// A sender for sharing information about an incoming stream request.
353
    pub(crate) incoming_sender: StreamReqSender,
354
    /// The hop to expect incoming stream requests from.
355
    ///
356
    /// Set to `None` if we are a relay.
357
    pub(crate) hop_num: Option<HopNum>,
358
    /// A [`CmdChecker`] for validating incoming streams.
359
    pub(crate) cmd_checker: AnyCmdChecker,
360
    /// An [`IncomingStreamRequestFilter`] for checking whether the user wants
361
    /// this request, or wants to reject it immediately.
362
    #[educe(Debug(ignore))]
363
    pub(crate) filter: Box<dyn IncomingStreamRequestFilter>,
364
}
365

            
366
#[cfg(test)]
367
mod test {
368
    // @@ begin test lint list maintained by maint/add_warning @@
369
    #![allow(clippy::bool_assert_comparison)]
370
    #![allow(clippy::clone_on_copy)]
371
    #![allow(clippy::dbg_macro)]
372
    #![allow(clippy::mixed_attributes_style)]
373
    #![allow(clippy::print_stderr)]
374
    #![allow(clippy::print_stdout)]
375
    #![allow(clippy::single_char_pattern)]
376
    #![allow(clippy::unwrap_used)]
377
    #![allow(clippy::unchecked_time_subtraction)]
378
    #![allow(clippy::useless_vec)]
379
    #![allow(clippy::needless_pass_by_value)]
380
    #![allow(clippy::string_slice)] // See arti#2571
381
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
382

            
383
    use tor_cell::relaycell::{
384
        AnyRelayMsgOuter, RelayCellFormat,
385
        msg::{Begin, BeginDir, Data, Resolve},
386
    };
387

            
388
    use super::*;
389

            
390
    #[test]
391
    fn incoming_cmd_checker() {
392
        // Convert an AnyRelayMsg to an UnparsedRelayCell.
393
        let u = |msg| {
394
            let body = AnyRelayMsgOuter::new(None, msg)
395
                .encode(RelayCellFormat::V0, &mut rand::rng())
396
                .unwrap();
397
            UnparsedRelayMsg::from_singleton_body(RelayCellFormat::V0, body).unwrap()
398
        };
399
        let begin = u(Begin::new("allium.example.com", 443, 0).unwrap().into());
400
        let begin_dir = u(BeginDir::default().into());
401
        let resolve = u(Resolve::new("allium.example.com").into());
402
        let data = u(Data::new(&[1, 2, 3]).unwrap().into());
403

            
404
        {
405
            let mut cc_none = IncomingCmdChecker::new_any(&[]);
406
            for m in [&begin, &begin_dir, &resolve, &data] {
407
                assert!(cc_none.check_msg(m).is_err());
408
            }
409
        }
410

            
411
        {
412
            let mut cc_begin = IncomingCmdChecker::new_any(&[RelayCmd::BEGIN]);
413
            assert_eq!(cc_begin.check_msg(&begin).unwrap(), StreamStatus::Open);
414
            for m in [&begin_dir, &resolve, &data] {
415
                assert!(cc_begin.check_msg(m).is_err());
416
            }
417
        }
418

            
419
        {
420
            let mut cc_any = IncomingCmdChecker::new_any(&[
421
                RelayCmd::BEGIN,
422
                RelayCmd::BEGIN_DIR,
423
                RelayCmd::RESOLVE,
424
            ]);
425
            for m in [&begin, &begin_dir, &resolve] {
426
                assert_eq!(cc_any.check_msg(m).unwrap(), StreamStatus::Open);
427
            }
428
            assert!(cc_any.check_msg(&data).is_err());
429
        }
430
    }
431
}