1
//! Channel for sending messages to [`StreamReactor`].
2

            
3
use crate::circuit::UniqId;
4
use crate::circuit::circhop::{CircHopOutbound, HopSettings};
5
use crate::circuit::reactor::circhop::CircHopList;
6
use crate::circuit::reactor::stream::{CtrlMsg, ReadyStreamMsg, StreamHandler, StreamReactor};
7
use crate::congestion::CongestionControl;
8
use crate::memquota::CircuitAccount;
9
use crate::util::err::ReactorError;
10
use crate::{Error, HopNum, Result};
11

            
12
#[cfg(any(feature = "hs-service", feature = "relay"))]
13
use {
14
    crate::stream::CloseStreamBehavior, crate::stream::incoming::IncomingStreamRequestHandler,
15
    tor_cell::relaycell::StreamId,
16
};
17

            
18
use tor_cell::chancell::CircId;
19
use tor_error::internal;
20
use tor_rtcompat::Runtime;
21

            
22
use futures::SinkExt;
23
use futures::channel::mpsc;
24

            
25
use std::result::Result as StdResult;
26
use std::sync::{Arc, Mutex, RwLock};
27

            
28
/// The hop manager of a reactor.
29
///
30
/// This contains the per-hop state (e.g. congestion control information),
31
/// and a handle to the stream reactor of the hop.
32
///
33
/// The stream reactor of the hop is launched lazily,
34
/// when the first [`CtrlMsg`] is sent via [`HopMgr::send`].
35
pub(crate) struct HopMgr<R: Runtime> {
36
    /// A handle to the runtime.
37
    runtime: R,
38
    /// Context used when spawning a stream reactor.
39
    ctx: StreamReactorContext,
40
    /// Sender for sending messages to BWD.
41
    ///
42
    /// The receiver is in BWD.
43
    ///
44
    /// A clone of this is passed to each spawned StreamReactor
45
    bwd_tx: mpsc::Sender<ReadyStreamMsg>,
46
    /// The underlying senders, indexed by [`HopNum`].
47
    ///
48
    /// Relays have at most one stream reactor per circuit.
49
    /// Clients have at most one stream reactor per circuit hop.
50
    ///
51
    /// This is shared with the backward reactor.
52
    /// The backward reactor only ever *reads* from this
53
    /// (it never mutates the list).
54
    ///
55
    // TODO: the backward reactor only ever reads from this.
56
    // Conceptually, it is the HopMgr that owns this list,
57
    // because only HopMgr can add hops to the list.
58
    //
59
    // Perhaps we need a specialized abstraction that only allows reading here.
60
    // This could be a wrapper over RwLock, providing a read-only API for the BWD.
61
    hops: Arc<RwLock<CircHopList>>,
62
    /// Memory quota account
63
    memquota: CircuitAccount,
64
}
65

            
66
/// State needed to build a stream reactor.
67
///
68
/// Used when spawning the stream reactor of a hop.
69
struct StreamReactorContext {
70
    /// An identifier for logging about this reactor's circuit.
71
    unique_id: UniqId,
72
    /// The circuit identifier on the inbound Tor channel.
73
    circ_id: CircId,
74
    /// The incoming stream handler.
75
    ///
76
    /// This is shared with every StreamReactor.
77
    #[cfg(any(feature = "hs-service", feature = "relay"))]
78
    incoming: Arc<Mutex<Option<IncomingStreamRequestHandler>>>,
79
    /// A handler for customizing the stream reactor behavior.
80
    handler: Arc<dyn StreamHandler>,
81
}
82

            
83
impl<R: Runtime> HopMgr<R> {
84
    /// Create a new [`HopMgr`] with an empty hop list,
85
    /// settings the incoming stream request handler to `incoming_handler`.
86
    ///
87
    /// Hops are added with [`HopMgr::add_hop`].
88
    #[cfg(feature = "relay")]
89
64
    pub(crate) fn new_with_incoming_handler<S: StreamHandler>(
90
64
        runtime: R,
91
64
        unique_id: UniqId,
92
64
        circ_id: CircId,
93
64
        handler: S,
94
64
        bwd_tx: mpsc::Sender<ReadyStreamMsg>,
95
64
        incoming_handler: IncomingStreamRequestHandler,
96
64
        memquota: CircuitAccount,
97
64
    ) -> Self {
98
64
        Self::new_inner(
99
64
            runtime,
100
64
            unique_id,
101
64
            circ_id,
102
64
            handler,
103
64
            bwd_tx,
104
64
            Some(incoming_handler),
105
64
            memquota,
106
        )
107
64
    }
108

            
109
    /// Create a new [`HopMgr`] with an empty hop list.
110
    ///
111
    /// Hops are added with [`HopMgr::add_hop`].
112
    #[expect(unused)] // TODO(dedup): clients will use this
113
    pub(crate) fn new<S: StreamHandler>(
114
        runtime: R,
115
        unique_id: UniqId,
116
        circ_id: CircId,
117
        handler: S,
118
        bwd_tx: mpsc::Sender<ReadyStreamMsg>,
119
        memquota: CircuitAccount,
120
    ) -> Self {
121
        Self::new_inner(
122
            runtime,
123
            unique_id,
124
            circ_id,
125
            handler,
126
            bwd_tx,
127
            #[cfg(any(feature = "hs-service", feature = "relay"))]
128
            None,
129
            memquota,
130
        )
131
    }
132

            
133
    /// Helper for the new*() functions.
134
64
    fn new_inner<S: StreamHandler>(
135
64
        runtime: R,
136
64
        unique_id: UniqId,
137
64
        circ_id: CircId,
138
64
        handler: S,
139
64
        bwd_tx: mpsc::Sender<ReadyStreamMsg>,
140
64
        #[cfg(any(feature = "hs-service", feature = "relay"))] incoming_handler: Option<
141
64
            IncomingStreamRequestHandler,
142
64
        >,
143
64
        memquota: CircuitAccount,
144
64
    ) -> Self {
145
        // We don't spawn any stream reactors ahead of time.
146
        // Instead we spawn them lazily, when opening streams.
147
64
        let hops = Arc::new(RwLock::new(Default::default()));
148
64
        let ctx = StreamReactorContext {
149
64
            unique_id,
150
64
            circ_id,
151
64
            #[cfg(any(feature = "hs-service", feature = "relay"))]
152
64
            incoming: Arc::new(Mutex::new(incoming_handler)),
153
64
            handler: Arc::new(handler),
154
64
        };
155

            
156
64
        Self {
157
64
            runtime,
158
64
            hops,
159
64
            ctx,
160
64
            bwd_tx,
161
64
            memquota,
162
64
        }
163
64
    }
164

            
165
    /// Return a reference to our hop list.
166
176
    pub(crate) fn hops(&self) -> &Arc<RwLock<CircHopList>> {
167
176
        &self.hops
168
176
    }
169

            
170
    /// Set the incoming stream handler for this reactor.
171
    ///
172
    /// There can only be one incoming stream handler per reactor,
173
    /// and each stream handler only pertains to a single hop (see expected_hop())
174
    //
175
    // TODO: eventually, we might want a different design here,
176
    // for example we might want to allow multiple stream handlers per reactor (one per hop).
177
    // However, for now, the implementation is intentionally kept similar to that
178
    // in the client reactor (to make it easier to migrate it to the new reactor design).
179
    //
180
    /// Returns an error if the hop manager already has a stream handler.
181
    ///
182
    /// Since the handler is shared with every hop's stream reactor,
183
    /// this function will update the handler for all of them.
184
    ///
185
    // TODO(DEDUP): almost identical to the client-side
186
    // CellHandlers::set_incoming_stream_req_handler()
187
    #[cfg(any(feature = "hs-service", feature = "relay"))]
188
    pub(crate) fn set_incoming_handler(&self, handler: IncomingStreamRequestHandler) -> Result<()> {
189
        let mut lock = self.ctx.incoming.lock().expect("poisoned lock");
190

            
191
        if lock.is_none() {
192
            *lock = Some(handler);
193
            Ok(())
194
        } else {
195
            Err(Error::from(internal!(
196
                "Tried to install a BEGIN cell handler before the old one was gone."
197
            )))
198
        }
199
    }
200

            
201
    /// Push a new hop to our hop list.
202
    ///
203
    /// Prepares a cc object for the hop, but does not spawn a stream reactor.
204
    ///
205
    /// Will return an error if the circuit already has [`u8::MAX`] hops.
206
64
    pub(crate) fn add_hop(&mut self, settings: HopSettings) -> Result<()> {
207
64
        let mut hops = self.hops.write().expect("poisoned lock");
208
64
        hops.add_hop(settings)
209
64
    }
210

            
211
    /// Send a message to the stream reactor of the specified `hop`,
212
    /// spawning it if necessary.
213
28
    pub(crate) async fn send(
214
28
        &mut self,
215
28
        hopnum: Option<HopNum>,
216
28
        msg: CtrlMsg,
217
28
    ) -> StdResult<(), ReactorError> {
218
28
        let mut tx = self.get_or_spawn_stream_reactor(hopnum)?;
219

            
220
28
        tx.send(msg).await.map_err(|_| {
221
            // The stream reactor has shut down
222
            ReactorError::Shutdown
223
        })
224
26
    }
225

            
226
    /// Tell the stream reactor of the specified `hop`
227
    /// to close the stream with the specified `stream_id`.
228
    #[cfg(any(feature = "hs-service", feature = "relay"))]
229
8
    pub(crate) async fn close_pending(
230
8
        &mut self,
231
8
        hopnum: Option<HopNum>,
232
8
        stream_id: StreamId,
233
8
        behav: CloseStreamBehavior,
234
8
    ) -> StdResult<(), Error> {
235
8
        let mut tx = self.get_or_spawn_stream_reactor(hopnum)?;
236

            
237
8
        let msg = CtrlMsg::ClosePendingStream { stream_id, behav };
238

            
239
8
        tx.send(msg).await.map_err(|_| {
240
            // The stream reactor has shut down
241
            Error::NotConnected
242
        })
243
8
    }
244

            
245
    /// Get a handle to the stream reactor, spawning it if necessary
246
36
    fn get_or_spawn_stream_reactor(
247
36
        &self,
248
36
        hopnum: Option<HopNum>,
249
36
    ) -> StdResult<mpsc::Sender<CtrlMsg>, Error> {
250
36
        let mut hops = self.hops.write().expect("poisoned lock");
251
36
        let hop = hops
252
36
            .get_mut(hopnum)
253
36
            .ok_or_else(|| internal!("tried to send cell to nonexistent hop?!"))?;
254

            
255
36
        let tx = match &hop.tx {
256
20
            Some(tx) => tx.clone(),
257
            None => {
258
                // If we don't have a handle to the stream reactor,
259
                // it means it hasn't been spawned yet, so we have to spawn it now.
260
16
                let tx =
261
16
                    self.spawn_stream_reactor(hopnum, &hop.settings, Arc::clone(&hop.ccontrol))?;
262

            
263
16
                hop.tx = Some(tx.clone());
264

            
265
                // Return a copy of this sender (can't borrow because the hop
266
                // is behind a Mutex, and we can't keep it locked across the send()
267
                // await point)
268
16
                tx
269
            }
270
        };
271

            
272
36
        Ok(tx)
273
36
    }
274

            
275
    /// Spawn a [`StreamReactor`] for the specified hop.
276
16
    fn spawn_stream_reactor(
277
16
        &self,
278
16
        hopnum: Option<HopNum>,
279
16
        settings: &HopSettings,
280
16
        ccontrol: Arc<Mutex<CongestionControl>>,
281
16
    ) -> StdResult<mpsc::Sender<CtrlMsg>, Error> {
282
        use tor_rtcompat::SpawnExt as _;
283

            
284
        // NOTE: not registering this channel with the memquota subsystem is okay,
285
        // because it has no buffering (if ever decide to make the size of this buffer
286
        // non-zero for whatever reason, we must remember to register it with memquota
287
        // so that it counts towards the total memory usage for the circuit.
288
        //
289
        // TODO(tuning): having zero buffering here is very likely suboptimal.
290
        // We should do *some* buffering here, and then figure out if we should it
291
        // up to memquota or not.
292
        #[allow(clippy::disallowed_methods)]
293
16
        let (fwd_stream_tx, fwd_stream_rx) = mpsc::channel(0);
294

            
295
16
        let flow_ctrl_params = Arc::new(settings.flow_ctrl_params.clone());
296
16
        let relay_format = settings.relay_crypt_protocol().relay_cell_format();
297
16
        let outbound = CircHopOutbound::new(ccontrol, relay_format, flow_ctrl_params, settings);
298

            
299
16
        let stream_reactor = StreamReactor::new(
300
16
            self.runtime.clone(),
301
16
            hopnum,
302
16
            outbound,
303
16
            self.ctx.unique_id,
304
16
            self.ctx.circ_id,
305
16
            fwd_stream_rx,
306
16
            self.bwd_tx.clone(),
307
16
            Arc::clone(&self.ctx.handler),
308
            #[cfg(any(feature = "hs-service", feature = "relay"))]
309
16
            Arc::clone(&self.ctx.incoming),
310
16
            self.memquota.clone(),
311
        );
312

            
313
16
        self.runtime
314
16
            .spawn(async {
315
16
                let _ = stream_reactor.run().await;
316
16
            })
317
16
            .map_err(|e| Error::Spawn {
318
                spawning: "stream reactor",
319
                cause: e.into(),
320
            })?;
321

            
322
16
        Ok(fwd_stream_tx)
323
16
    }
324
}