1
//! Types for building circuits, or otherwise acting as a "client".
2

            
3
use std::sync::Arc;
4

            
5
use anyhow::Context;
6
use tor_chanmgr::ChanMgr;
7
use tor_circmgr::{CircMgr, CircMgrConfig};
8
use tor_dirmgr::{DirMgr, DirMgrConfig, DirMgrStore, DirPlugin, DirProvider};
9
use tor_guardmgr::{GuardMgr, GuardMgrConfig};
10
use tor_persist::FsStateMgr;
11
use tor_rtcompat::Runtime;
12
use tor_rtcompat::scheduler::TaskHandle;
13

            
14
/// A "client" used by relays to construct circuits. For example a relay needs to build
15
/// bandwidth-testing circuits, reachability-testing circuits, and possibly in the future anonymous
16
/// circuits.
17
///
18
/// The idea here is that this [`RelayClient`] will encapsulate everything needed for building
19
/// circuits. So the relay itself doesn't need to worry about a channel manager, guard manager, etc.
20
/// Instead we provide methods here for building whatever circuits the relay may need, and with
21
/// whatever properties the relay needs.
22
pub(crate) struct RelayClient<R: Runtime> {
23
    /// The provided runtime.
24
    runtime: R,
25

            
26
    /// The provided state manager.
27
    state_mgr: FsStateMgr,
28

            
29
    /// Channel manager, used by circuits etc.
30
    #[expect(unused)] // TODO RELAY remove
31
    chanmgr: Arc<ChanMgr<R>>,
32

            
33
    /// Guard manager.
34
    #[expect(unused)] // TODO RELAY remove
35
    guardmgr: GuardMgr<R>,
36

            
37
    /// Circuit manager for keeping our circuits up to date and building
38
    /// them on-demand.
39
    circmgr: Arc<CircMgr<R>>,
40

            
41
    /// Directory manager for keeping our directory material up to date.
42
    dirmgr: Arc<dyn DirProvider>,
43

            
44
    /// A dirmgr-derived plugin for a directory mirror.
45
    ///
46
    /// TODO: This has nothing to do with the client,
47
    /// but we need it temporarily until the directory mirror is implemented.
48
    /// We should remove this later.
49
    dirmgr_plugin: DirPlugin,
50
}
51

            
52
impl<R: Runtime> RelayClient<R> {
53
    /// Create a new [`RelayClient`].
54
    ///
55
    /// You must call [`RelayClient::launch_background_tasks()`] before using.
56
    pub(crate) fn new(
57
        runtime: R,
58
        chanmgr: Arc<ChanMgr<R>>,
59
        guardmgr_config: &impl GuardMgrConfig,
60
        circmgr_config: &impl CircMgrConfig,
61
        dirmgr_config: DirMgrConfig,
62
        state_mgr: FsStateMgr,
63
    ) -> anyhow::Result<Self> {
64
        // TODO: We probably don't want a guard manager for relays,
65
        // unless we plan to build anonymous circuits.
66
        // See https://gitlab.torproject.org/tpo/core/arti/-/issues/1737.
67
        // If we do want a guard manager and anonymous circuits,
68
        // we should think more about whether our anonymous circuits can be differentiated from
69
        // other circuits, and make sure that we're not closing channels for "client reasons" as
70
        // these channels will also be used by the relay for relaying Tor user traffic.
71
        // See https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/3552#note_3313591.
72
        let guardmgr = GuardMgr::new(runtime.clone(), state_mgr.clone(), guardmgr_config)
73
            .context("Failed to initialize the guard manager")?;
74

            
75
        // TODO: We might not want a circuit manager for relays,
76
        // but we will probably want its path construction logic.
77
        // We need to be able to build circuits for reachability testing and bandwidth measurement.
78
        let circmgr = Arc::new(
79
            CircMgr::new(
80
                circmgr_config,
81
                state_mgr.clone(),
82
                &runtime,
83
                Arc::clone(&chanmgr),
84
                &guardmgr,
85
            )
86
            .context("Failed to initialize the circuit manager")?,
87
        );
88

            
89
        let dirmgr_store =
90
            DirMgrStore::new(&dirmgr_config, runtime.clone(), /* offline= */ false)
91
                .context("Failed to initialize directory store")?;
92

            
93
        // TODO: We want to use tor-dirserver as a `NetDirProvider` in the future if possible to
94
        // avoid having two document downloaders, and so that we can download documents over direct
95
        // TCP connections rather than over circuits.
96
        let dirmgr = Arc::new(
97
            DirMgr::create_unbootstrapped(
98
                dirmgr_config,
99
                runtime.clone(),
100
                dirmgr_store,
101
                Arc::clone(&circmgr),
102
            )
103
            .context("Failed to initialize the directory manager")?,
104
        );
105

            
106
        // Plugin for the relay's directory mirror.
107
        let dirmgr_plugin = dirmgr.get_plugin();
108

            
109
        Ok(Self {
110
            runtime,
111
            state_mgr,
112
            chanmgr,
113
            guardmgr,
114
            circmgr,
115
            dirmgr,
116
            dirmgr_plugin,
117
        })
118
    }
119

            
120
    /// Launch background tasks for any of the client's submodules.
121
    ///
122
    /// The background tasks will stop when the returned [`TaskHandle`]s are dropped.
123
    pub(crate) fn launch_background_tasks(&self) -> anyhow::Result<Vec<TaskHandle>> {
124
        self.circmgr
125
            .launch_background_tasks(&self.runtime, &self.dirmgr, self.state_mgr.clone())
126
            .context("Failed to launch circuit manager background tasks")
127
    }
128

            
129
    /// Bootstrap this client by ensuring we have directory documents downloaded.
130
    ///
131
    /// TODO: We want to use tor-dirserver as a `NetDirProvider` in the future, so hopefully we won't
132
    /// need this `bootstrap()` method as directory downloads will be performed elsewhere.
133
    pub(crate) async fn bootstrap(&self) -> anyhow::Result<()> {
134
        self.dirmgr
135
            .bootstrap()
136
            .await
137
            .context("Failed to bootstrap the directory manager")?;
138

            
139
        Ok(())
140
    }
141

            
142
    /// Get the client's [`DirProvider`].
143
    pub(crate) fn dirmgr(&self) -> &Arc<dyn DirProvider> {
144
        &self.dirmgr
145
    }
146

            
147
    /// Temporary. See [`RelayClient::dirmgr_plugin`].
148
    pub(crate) fn dirmgr_plugin(&self) -> &DirPlugin {
149
        &self.dirmgr_plugin
150
    }
151
}