1
//! Stream handling logic
2

            
3
mod directory;
4
pub(crate) mod dns;
5
mod exit;
6

            
7
use dns::resolver::DnsResolver;
8

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

            
18
use futures::channel::mpsc;
19
use futures::{Stream, StreamExt as _};
20

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

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

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

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

            
63
    Err(anyhow::anyhow!("stream handling task exited"))
64
}
65

            
66
/// Handle all the incoming stream requests (BEGIN, BEGIN_DIR, or RESOLVE)
67
/// arriving on a particular circuit.
68
async fn handle_circuit_incoming_streams<R: Runtime>(
69
    runtime: R,
70
    mut stream: impl Stream<Item = IncomingStream> + Unpin,
71
    begin_dir_tx: mpsc::Sender<tor_proto::Result<DataStream>>,
72
    resolver: DnsResolver,
73
) {
74
    while let Some(tor_stream) = stream.next().await {
75
        let begin_dir_tx = begin_dir_tx.clone();
76

            
77
        let resolver = resolver.clone();
78
        // Spawn a new task for each individual stream
79
        if let Err(e) = runtime.spawn(async move {
80
            let res = match tor_stream.request() {
81
                IncomingStreamRequest::Begin(_) => exit::handle_begin(tor_stream).await,
82
                IncomingStreamRequest::BeginDir(_) => {
83
                    directory::handle_begin_dir(tor_stream, begin_dir_tx).await
84
                }
85
                IncomingStreamRequest::Resolve(_) => {
86
                    dns::handle_resolve(tor_stream, resolver.clone()).await
87
                }
88
                s => Err(anyhow::anyhow!("unknown stream request kind {s:?}")),
89
            };
90

            
91
            if let Err(e) = res {
92
                warn_report!(e, "Could not handle incoming stream");
93
            }
94
        }) {
95
            warn_report!(e, "Failed to launch incoming stream handler task");
96
        }
97
    }
98
}