1
//! Relay descriptor publishing task.
2
//!
3
//! This task is responsible for building our relay descriptor and uploading it to the directory
4
//! authorities using [`tor_dirpublish`].
5
//!
6
//! It is driven by two sources of input:
7
//!   * When receiving a [`DescriptorCommand`]. For instance, the crypto task
8
//!     ([`crate::tasks::crypto`]) will send a [`DescriptorCommand::Publish`] when at least
9
//!     one of the relay keys changes. It triggers a rebuild and publish of the relay descriptor.
10
//!   * Consensus events from the [`NetDirProvider`], so that we can pick up new consensus
11
//!     parameters when a new consensus arrives.
12
use std::collections::HashSet;
13
use std::net::SocketAddr;
14
use std::sync::Arc;
15
use std::time::Duration;
16

            
17
use anyhow::Context;
18
use futures::channel::mpsc;
19
use futures::{StreamExt as _, select_biased};
20
use tracing::{debug, trace};
21

            
22
use tor_async_utils::{mpsc_channel_no_memquota, oneshot};
23
use tor_dirclient::request::{Requestable, UploadRouterDesc};
24
use tor_dircommon::authority::AuthorityContacts;
25
use tor_dirpublish::{Publisher, http::DirectHttpUploader};
26
use tor_netdir::{DirEvent, NetDirProvider};
27
use tor_rtcompat::Runtime;
28

            
29
use crate::tasks::crypto::{CryptoCommand, CryptoCommandSender};
30

            
31
/// Initial delay before retrying a failed descriptor upload.
32
///
33
/// This is simply the initial delay then the [`tor_dirpublish::Publisher`] has its back off
34
/// algorithm seeded with this value.
35
const INITIAL_RETRY_DELAY: Duration = Duration::from_secs(60);
36

            
37
/// A command sent to the [`RelayDescriptorPublisherTask`] over its control channel.
38
#[derive(Clone, Debug)]
39
#[non_exhaustive]
40
pub(crate) enum DescriptorCommand {
41
    /// Rebuild the relay descriptor and publish it to the directory authorities. This
42
    /// task asks the crypto task for the latest known identities/keys in order to
43
    /// rebuild a new descriptor.
44
    Publish,
45
}
46

            
47
/// The sending side of the [`DescriptorCommand`] channel.
48
pub(crate) type DescriptorCommandSender = mpsc::Sender<DescriptorCommand>;
49

            
50
/// The receiving side of the [`DescriptorCommand`] channel.
51
pub(crate) type DescriptorCommandReceiver = mpsc::Receiver<DescriptorCommand>;
52

            
53
/// Returns a new [`DescriptorCommand`] channel.
54
///
55
/// This is a bounded to limit descriptor publication spamming (in case of a bug).
56
pub(crate) fn new_command_channel() -> (DescriptorCommandSender, DescriptorCommandReceiver) {
57
    // TODO(relay): We might want to make those memquota actually?
58
    mpsc_channel_no_memquota(16)
59
}
60

            
61
/// Background task that builds and publishes the relay's descriptor.
62
pub(crate) struct RelayDescriptorPublisherTask {
63
    /// Directory provider, used to learn about new consensus documents and parameters.
64
    netdir: Arc<dyn NetDirProvider>,
65

            
66
    /// The directory authorities we upload our descriptor to.
67
    ///
68
    /// This is either from the config file or the compiled-in default list.
69
    authorities: AuthorityContacts,
70

            
71
    /// Channel on which we receive [`DescriptorCommand`]s from other tasks.
72
    command_rx: DescriptorCommandReceiver,
73

            
74
    /// The [`tor_dirpublish`] publisher that manages uploads to all targets.
75
    publisher: Arc<Publisher<dyn Requestable, Vec<SocketAddr>>>,
76

            
77
    /// The crypto task sender channel.
78
    crypto_tx: CryptoCommandSender,
79
}
80

            
81
impl RelayDescriptorPublisherTask {
82
    /// Construct a new descriptor publisher task.
83
    ///
84
    /// This launches the underlying [`tor_dirpublish`] publisher (which spawns its own reactor),
85
    /// but does not start listening for commands or consensus events until start() is called.
86
    ///
87
    /// The publisher reactor won't try to upload until the
88
    /// [`tor_dirpublish::Publisher::set_document`] is called.
89
    pub(crate) fn new<R: Runtime>(
90
        runtime: &R,
91
        netdir: Arc<dyn NetDirProvider>,
92
        authorities: AuthorityContacts,
93
        crypto_tx: CryptoCommandSender,
94
        command_rx: DescriptorCommandReceiver,
95
    ) -> anyhow::Result<Self> {
96
        let uploader = Arc::new(DirectHttpUploader::new(runtime.clone()));
97

            
98
        // We start with no document and no targets. Both are populated once we build a descriptor.
99
        // This way we catch any new directory authorities showing up in the config or consensus.
100
        let publisher = Publisher::launch(
101
            runtime,
102
            "relay descriptor".to_string(),
103
            /* initial_document=*/ None,
104
            /* initial_targets=*/ HashSet::new(),
105
            INITIAL_RETRY_DELAY,
106
            uploader,
107
        )
108
        .context("Failed to launch descriptor publisher")?;
109

            
110
        Ok(Self {
111
            netdir,
112
            authorities,
113
            command_rx,
114
            publisher,
115
            crypto_tx,
116
        })
117
    }
118

            
119
    /// Build the relay's descriptor document as ready to be uploaded.
120
    ///
121
    /// Returns `None` if we don't have everything we need to build a descriptor.
122
    #[allow(clippy::unused_async)] // TODO(relay): remove once used.
123
    async fn build_descriptor(&mut self) -> anyhow::Result<Option<Arc<str>>> {
124
        // TODO(relay): No relay desc encoding support yet from tor-netdoc.
125
        //
126
        // Once encoding exists, this should:
127
        //   * encode and sign the descriptor,
128

            
129
        // Get the latest ntor key (onion key) from the crypto task.
130
        let (tx, rx) = oneshot::channel();
131
        self.crypto_tx
132
            .try_send(CryptoCommand::GetLatestNtorKey { tx })
133
            .context("Crypto task try_send failed")?;
134
        let _ntor_key = rx.await.context("Unable to get ntor key")?;
135

            
136
        // Get the relay signing key from the crypto task.
137
        let (tx, rx) = oneshot::channel();
138
        self.crypto_tx
139
            .try_send(CryptoCommand::GetSignKey { tx })
140
            .context("Crypto task try_send failed")?;
141
        let _relay_sign_kp = rx.await.context("Unable to get relay sign keypair")?;
142

            
143
        // Keep the publisher idle until descriptor encoding is implemented.
144
        Ok(None)
145
    }
146

            
147
    /// Recompute the set of directory authorities we upload to.
148
    ///
149
    /// Each authority becomes one target, carrying all of its upload addresses so the
150
    /// [`DirectHttpUploader`] can try them in turn.
151
    ///
152
    /// Returns an empty set if we somehow have no authorities at all.
153
    fn compute_targets(&self) -> HashSet<Vec<SocketAddr>> {
154
        // This should never be empty because we have compiled in authorities by default.
155
        // If that case ever happens, the publisher will just do nothing.
156
        //
157
        // TODO(relay): We have to check those against our relay capabilities as in if we
158
        // support IPv6 or if we have an IPv4. For now, we pass all targets and let any
159
        // failures be handled at the connect() attempt.
160
        self.authorities
161
            .uploads()
162
            .iter()
163
            .filter(|&addrs| !addrs.is_empty())
164
            .cloned()
165
            .collect()
166
    }
167

            
168
    /// Rebuild the descriptor (and refresh targets) and hand it to the publisher.
169
    async fn rebuild_and_publish(&mut self) -> anyhow::Result<()> {
170
        // Adjust the targets onto our publisher if we have any targets. An empty set
171
        // means something has gone wrong somehow so don't touch the publisher in an
172
        // attempt to use what was there before.
173
        let targets = self.compute_targets();
174
        if !targets.is_empty() {
175
            let targets = targets.into_iter().map(Arc::new).collect();
176
            self.publisher.adjust_targets(|t| *t = targets);
177
        }
178

            
179
        // Get the latest descriptor.
180
        let desc = self
181
            .build_descriptor()
182
            .await
183
            .context("Failed to build relay descriptor")?;
184
        // Turn the encoded descriptor into a request and erase its concrete type for the
185
        // generic HTTP publisher.
186
        let doc = desc.map(|desc| Arc::new(UploadRouterDesc::new(desc)) as Arc<dyn Requestable>);
187

            
188
        // Tell the publisher to publish the new document. Failing to build the descriptor, as in a
189
        // None value, will make the publisher wait and do nothing.
190
        self.publisher.set_document(doc, false);
191
        Ok(())
192
    }
193

            
194
    /// Start the task.
195
    ///
196
    /// This runs forever. It listens for [`DescriptorCommand`] and consensus events.
197
    pub(crate) async fn start(mut self) -> anyhow::Result<void::Void> {
198
        debug!("Starting Relay descriptor publisher task");
199

            
200
        // Subscribe before the first run so we don't miss any events that arrive between
201
        // startup and entering the select loop.
202
        let mut consensus_events = self
203
            .netdir
204
            .events()
205
            .filter(|ev| std::future::ready(matches!(ev, DirEvent::NewConsensus)))
206
            .fuse();
207

            
208
        // Do an initial build now, in case we already have a consensus.
209
        self.rebuild_and_publish()
210
            .await
211
            .context("Failed initial descriptor publish")?;
212

            
213
        loop {
214
            select_biased! {
215
                command = self.command_rx.next() => {
216
                    let command = command
217
                        .context("descriptor command channel closed unexpectedly")?;
218
                    trace!(?command, "Descriptor publisher received command");
219
                    match command {
220
                        DescriptorCommand::Publish => {
221
                            self.rebuild_and_publish()
222
                                .await
223
                                .context("Failed to publish descriptor on command")?;
224
                        }
225
                    }
226
                }
227
                event = consensus_events.next() => {
228
                    let _event = event
229
                        .context("netdir consensus event stream ended unexpectedly")?;
230
                    trace!("Descriptor publisher task saw new consensus. Rebuilding and publishing.");
231
                    self.rebuild_and_publish()
232
                        .await
233
                        .context("Failed to publish descriptor on new consensus")?;
234
                }
235
                // TODO(relay)
236
                //
237
                // Here are the other conditions documented in the spec for when we
238
                // upload a new descriptor:
239
                // https://spec.torproject.org/dir-spec/uploading-relay-documents.html
240
                //
241
                //  - A period of time (18 hrs by default) has passed since the last
242
                //  upload.
243
                //  - A descriptor field other than bandwidth or uptime has changed.
244
                //  Its uptime is less than 24h and bandwidth has changed by a factor of
245
                //  2 from the last time a descriptor was generated, and at least a given
246
                //  interval of time (3 hours by default) has passed since then.
247
                //  - Its uptime has been reset (by restarting).
248
                //  - It receives a networkstatus consensus in which it is not listed.
249
                //  - It receives a networkstatus consensus in which it is listed with
250
                //  the StaleDesc flag.
251
            }
252
        }
253
    }
254
}