1
//! Stream handling logic
2

            
3
mod directory;
4
mod dns;
5
mod exit;
6

            
7
use tor_error::warn_report;
8
use tor_proto::circuit::CircHopSyncView;
9
use tor_proto::relay::CircuitIncomingStreamReceiver;
10
use tor_proto::stream::{
11
    IncomingStream, IncomingStreamRequest, IncomingStreamRequestContext,
12
    IncomingStreamRequestDisposition, IncomingStreamRequestFilter,
13
};
14
use tor_rtcompat::{Runtime, SpawnExt as _};
15

            
16
use futures::channel::mpsc;
17
use futures::{Stream, StreamExt as _};
18

            
19
// TODO(#2570): once we dust settles on the implementation,
20
// we need to factor this out of the client module.
21
use tor_proto::client::stream::DataStream;
22

            
23
/// Filter callback used to enforce early requirements on streams,
24
/// acting as an [`IncomingStreamRequestFilter`].
25
#[derive(Clone, Debug, Default)]
26
pub(crate) struct RequestFilter {
27
    // TODO(relay): implement
28
}
29

            
30
impl IncomingStreamRequestFilter for RequestFilter {
31
    fn disposition(
32
        &mut self,
33
        _ctx: &IncomingStreamRequestContext<'_>,
34
        _circ: &CircHopSyncView<'_>,
35
    ) -> tor_proto::Result<IncomingStreamRequestDisposition> {
36
        // TODO(relay): enforce the checks mentioned in relay-streams.md
37
        Ok(IncomingStreamRequestDisposition::Accept)
38
    }
39
}
40

            
41
/// Handle all the incoming streams arriving on all the circuits
42
pub(crate) async fn handle_incoming_streams<R: Runtime>(
43
    runtime: R,
44
    begin_dir_tx: mpsc::Sender<tor_proto::Result<DataStream>>,
45
    mut stream_rx: CircuitIncomingStreamReceiver,
46
) -> anyhow::Result<void::Void> {
47
    while let Some(stream) = stream_rx.next().await {
48
        // Each circuit gets its own stream-handling task
49
        let rt = runtime.clone();
50
        let begin_dir_tx = begin_dir_tx.clone();
51
        runtime.spawn(handle_circuit_incoming_streams(rt, stream, begin_dir_tx))?;
52
    }
53

            
54
    Err(anyhow::anyhow!("stream handling task exited"))
55
}
56

            
57
/// Handle all the incoming stream requests (BEGIN, BEGIN_DIR, or RESOLVE)
58
/// arriving on a particular circuit.
59
async fn handle_circuit_incoming_streams<R: Runtime>(
60
    runtime: R,
61
    mut stream: impl Stream<Item = IncomingStream> + Unpin,
62
    begin_dir_tx: mpsc::Sender<tor_proto::Result<DataStream>>,
63
) {
64
    while let Some(tor_stream) = stream.next().await {
65
        let begin_dir_tx = begin_dir_tx.clone();
66

            
67
        // Spawn a new task for each individual stream
68
        if let Err(e) = runtime.spawn(async move {
69
            let res = match tor_stream.request() {
70
                IncomingStreamRequest::Begin(_) => exit::handle_begin(tor_stream).await,
71
                IncomingStreamRequest::BeginDir(_) => {
72
                    directory::handle_begin_dir(tor_stream, begin_dir_tx).await
73
                }
74
                IncomingStreamRequest::Resolve(_) => dns::handle_resolve(tor_stream).await,
75
                s => Err(anyhow::anyhow!("unknown stream request kind {s:?}")),
76
            };
77

            
78
            if let Err(e) = res {
79
                warn_report!(e, "Could not handle incoming stream");
80
            }
81
        }) {
82
            warn_report!(e, "Failed to launch incoming stream handler task");
83
        }
84
    }
85
}