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, 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

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

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

            
82
        let dirmgr_store =
83
            DirMgrStore::new(&dirmgr_config, runtime.clone(), /* offline= */ false)
84
                .context("Failed to initialize directory store")?;
85

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

            
99
        Ok(Self {
100
            runtime,
101
            state_mgr,
102
            chanmgr,
103
            guardmgr,
104
            circmgr,
105
            dirmgr,
106
        })
107
    }
108

            
109
    /// Launch background tasks for any of the client's submodules.
110
    ///
111
    /// The background tasks will stop when the returned [`TaskHandle`]s are dropped.
112
    pub(crate) fn launch_background_tasks(&self) -> anyhow::Result<Vec<TaskHandle>> {
113
        self.circmgr
114
            .launch_background_tasks(&self.runtime, &self.dirmgr, self.state_mgr.clone())
115
            .context("Failed to launch circuit manager background tasks")
116
    }
117

            
118
    /// Bootstrap this client by ensuring we have directory documents downloaded.
119
    ///
120
    /// TODO: We want to use tor-dirserver as a `NetDirProvider` in the future, so hopefully we won't
121
    /// need this `bootstrap()` method as directory downloads will be performed elsewhere.
122
    pub(crate) async fn bootstrap(&self) -> anyhow::Result<()> {
123
        self.dirmgr
124
            .bootstrap()
125
            .await
126
            .context("Failed to bootstrap the directory manager")?;
127

            
128
        Ok(())
129
    }
130
}