aboutsummaryrefslogtreecommitdiff
path: root/crates/tor-proto/src/stream/incoming.rs
blob: 8345019fa2cd7712fad49df0d9dc9d328a3f1800 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
//! Functionality for incoming streams, opened from the other side of a circuit.

#![allow(dead_code, unused_variables, clippy::needless_pass_by_value)] // TODO hss remove

use super::{AnyCmdChecker, DataStream, StreamReader, StreamStatus};
use crate::circuit::StreamTarget;
use crate::{Error, Result};
use futures::channel::oneshot;
use std::result::Result as StdResult;
use tor_cell::relaycell::{msg, RelayCmd, UnparsedRelayCell};
use tor_cell::restricted_msg;
use tor_error::{internal, Bug};

/// A pending request from the other end of the circuit for us to open a new
/// stream.
///
/// Exits, directory caches, and onion services expect to receive these; others
/// do not.
///
/// On receiving one of these objects, the party handling it should accept it or
/// reject it.  If it is dropped without being explicitly handled, a reject
/// message will be sent anyway.
#[derive(Debug)]
pub struct IncomingStream {
    /// The message that the client sent us to begin the stream.
    request: IncomingStreamRequest,
    /// The inner state, which contains the reader and writer of this stream.
    ///
    /// This is an `Option` because we need to be able to "take" the reader/writer of the stream
    /// out of the `IncomingStream` to construct a [`DataStream`] in [`IncomingStream::accept_data`].
    ///
    /// Note: we can't move the reader/writer out of `self` because `IncomingStream` implements
    /// `Drop` (so as a workaround we use [`Option::take`]).
    inner: Option<IncomingStreamInner>,
    /// The state of the stream.
    state: IncomingStreamState,
}

/// The state of an [`IncomingStream`].
///
/// Only following transitions are allowed:
///
/// ```ignore
///
///                       accept_data()  +----------+
///                     +--------------->| Accepted |
///                     |                +----------+
///                     |
/// +---------+         | reject()       +----------+
/// | Pending |---------+--------------->| Rejected |
/// +---------+         |                +----------+
///                     |
///                     | discard()      +-----------+
///                     +--------------->| Discarded |
///                                      +-----------+
/// ```
#[derive(Copy, Clone, Debug, PartialEq, Default, derive_more::Display)]
enum IncomingStreamState {
    /// The initial state of an [`IncomingStream`].
    #[default]
    Pending,
    /// The state entered after a call to [`IncomingStream::accept_data`].
    Accepted,
    /// The state entered after a call to [`IncomingStream::reject`].
    Rejected,
    /// The state entered after a call to [`IncomingStream::discard`].
    Discarded,
}

/// The inner state of an [`IncomingStream`], which contains its reader and writer.
#[derive(Debug)]
struct IncomingStreamInner {
    /// The information that we'll use to wire up the stream, if it is accepted.
    stream: StreamTarget,
    /// The underlying `StreamReader`.
    reader: StreamReader,
}

impl IncomingStream {
    /// Create a new `IncomingStream`.
    pub(crate) fn new(
        request: IncomingStreamRequest,
        stream: StreamTarget,
        reader: StreamReader,
    ) -> Self {
        let inner = IncomingStreamInner { stream, reader };
        Self {
            request,
            inner: Some(inner),
            state: IncomingStreamState::default(),
        }
    }

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

    /// Whether we have rejected this `IncomingStream` using [`IncomingStream::reject`].
    pub fn is_rejected(&self) -> bool {
        self.state == IncomingStreamState::Rejected
    }

    /// Accept this stream as a new [`DataStream`], and send the client a
    /// message letting them know the stream was accepted.
    pub async fn accept_data(mut self, message: msg::Connected) -> Result<DataStream> {
        self.update_state(IncomingStreamState::Accepted, "accept_data")?;

        let mut inner = self.take_inner()?;

        match self.request {
            IncomingStreamRequest::Begin(_) | IncomingStreamRequest::BeginDir(_) => {
                inner.stream.send(message.into()).await?;
                Ok(DataStream::new_connected(inner.reader, inner.stream))
            }
            IncomingStreamRequest::Resolve(_) => {
                Err(internal!("Cannot accept data on a RESOLVE stream").into())
            }
        }
    }

    /// Reject this request and send an error message to the client.
    pub async fn reject(&mut self, message: msg::End) -> Result<()> {
        let rx = self.reject_inner(message)?;

        rx.await.map_err(|_| Error::CircuitClosed)?.map(|_| ())
    }

    /// Reject this request and send an error message to the client.
    ///
    /// Returns a [`oneshot::Receiver`] that can be used to await the reactor's response.
    ///
    /// This is used for implementing `Drop`.
    fn reject_inner(&mut self, message: msg::End) -> Result<oneshot::Receiver<Result<()>>> {
        self.update_state(IncomingStreamState::Rejected, "reject_inner")?;

        self.mut_inner()?.stream.close(message)
    }

    /// Ignore this request without replying to the client.
    ///
    /// (If you drop an [`IncomingStream`] without calling `accept_data`,
    /// `reject`, or this method, the drop handler will cause it to be
    /// rejected.)
    pub fn discard(mut self) -> StdResult<(), Bug> {
        self.update_state(IncomingStreamState::Discarded, "discard")
    }

    /// Try to update the state of this `IncomingStream` to `new_state`, returning an error if the
    /// requested transition is not allowed.
    fn update_state(&mut self, new_state: IncomingStreamState, caller: &str) -> StdResult<(), Bug> {
        use IncomingStreamState::*;

        match self.state {
            Pending => {
                self.state = new_state;
                Ok(())
            }
            _ => Err(internal!(
                "IncomingStream::{caller}() cannot be called on a {} stream",
                self.state
            )),
        }
    }

    /// Take the inner state out of `IncomingStream`.
    ///
    /// Returns an error if `inner` is `None` (this should never happen unless we have a bug in our
    /// code).
    fn take_inner(&mut self) -> Result<IncomingStreamInner> {
        let _: &mut _ = self.mut_inner()?;

        Ok(self
            .inner
            .take()
            .expect("inner None though we just checked it"))
    }

    /// Return a mutable reference to the inner state of `IncomingStream`.
    ///
    /// Returns an error if `inner` is `None` (this should never happen unless we have a bug in our
    /// code).
    fn mut_inner(&mut self) -> Result<&mut IncomingStreamInner> {
        self.inner
            .as_mut()
            .ok_or_else(|| internal!("Cannot use a stream that has already been consumed").into())
    }
}

impl Drop for IncomingStream {
    fn drop(&mut self) {
        if self.state == IncomingStreamState::Pending {
            // Disregard any errors.
            let _: Result<oneshot::Receiver<Result<()>>> = self.reject_inner(msg::End::new_misc());
        }
    }
}

restricted_msg! {
    /// The allowed incoming messages on an `IncomingStream`.
    #[derive(Clone, Debug)]
    #[non_exhaustive]
    pub enum IncomingStreamRequest: RelayMsg {
        /// A BEGIN message.
        Begin,
        /// A BEGIN_DIR message.
        BeginDir,
        /// A RESOLVE message.
        Resolve,
    }
}

/// A `CmdChecker` that enforces correctness for incoming commands on unrecognized streams that
/// have a non-zero stream ID.
#[derive(Debug)]
pub(crate) struct IncomingCmdChecker {
    /// The "begin" commands that can be received on this type of circuit:
    ///
    ///   * onion service circuits only accept `BEGIN`
    ///   * all relay circuits accept `BEGIN_DIR`
    ///   * exit relays additionally accept `BEGIN` or `RESOLVE` on relay circuits
    ///   * once CONNECT_UDP is implemented, relays and later onion services may accept CONNECT_UDP
    ///   as well
    allow_commands: Vec<RelayCmd>,
}

impl IncomingCmdChecker {
    /// Create a new boxed `IncomingCmdChecker`.
    pub(crate) fn new_any(allow_commands: &[RelayCmd]) -> AnyCmdChecker {
        // TODO HSS: avoid allocating a vec here
        Box::new(Self {
            allow_commands: allow_commands.to_vec(),
        })
    }
}

impl super::CmdChecker for IncomingCmdChecker {
    fn check_msg(&mut self, msg: &UnparsedRelayCell) -> Result<StreamStatus> {
        match msg.cmd() {
            cmd if self.allow_commands.contains(&cmd) => Ok(StreamStatus::Open),
            _ => Err(Error::StreamProto(format!(
                "Unexpected {} on incoming stream",
                msg.cmd()
            ))),
        }
    }

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

        Ok(())
    }
}