summaryrefslogtreecommitdiff
path: root/crates/tor-hsservice/src/svc/publish.rs
blob: 8d6cf4e4efc762a32dba1c3057ed7e69e2165cbc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! Publish and maintain onion service descriptors

#![allow(clippy::needless_pass_by_value)] // TODO HSS REMOVE.

mod backoff;
mod descriptor;
mod reactor;

use futures::task::SpawnExt;
use postage::watch;
use std::sync::Arc;
use tor_keymgr::KeyMgr;

use tor_circmgr::hspool::HsCircPool;
use tor_hscrypto::pk::HsId;
use tor_netdir::NetDirProvider;
use tor_rtcompat::Runtime;

use crate::OnionServiceConfig;
use crate::{ipt_set::IptsPublisherView, StartupError};

use reactor::{Reactor, ReactorError, ReactorState};

/// A handle for the Hsdir Publisher for an onion service.
///
/// This handle represents a set of tasks that identify the hsdirs for each
/// relevant time period, construct descriptors, publish them, and keep them
/// up-to-date.
#[must_use = "If you don't call launch() on the publisher, it won't publish any descriptors."]
pub(crate) struct Publisher<R: Runtime> {
    /// The runtime.
    runtime: R,
    /// The HsId of the service.
    //
    // TODO HSS: read this from the KeyMgr instead?
    hsid: HsId,
    /// A source for new network directories that we use to determine
    /// our HsDirs.
    dir_provider: Arc<dyn NetDirProvider>,
    /// A [`HsCircPool`] for building circuits to HSDirs.
    circpool: Arc<HsCircPool<R>>,
    /// The onion service config.
    config: Arc<OnionServiceConfig>,
    /// A channel for receiving IPT change notifications.
    ipt_watcher: IptsPublisherView,
    /// A channel for receiving onion service config change notifications.
    config_rx: watch::Receiver<Arc<OnionServiceConfig>>,
    /// The key manager.
    keymgr: Arc<KeyMgr>,
}

impl<R: Runtime> Publisher<R> {
    /// Create a new publisher.
    ///
    /// When it launches, it will know no keys or introduction points,
    /// and will therefore not upload any descriptors.
    ///
    /// The publisher won't start publishing until you call [`Publisher::launch`].
    //
    // TODO HSS: perhaps we don't need both config and config_rx (we could read the initial config
    // value from config_rx).
    #[allow(clippy::too_many_arguments)]
    pub(crate) fn new(
        runtime: R,
        hsid: HsId,
        dir_provider: Arc<dyn NetDirProvider>,
        circpool: Arc<HsCircPool<R>>,
        ipt_watcher: IptsPublisherView,
        config_rx: watch::Receiver<Arc<OnionServiceConfig>>,
        keymgr: Arc<KeyMgr>,
    ) -> Self {
        let config = config_rx.borrow().clone();
        Self {
            runtime,
            hsid,
            dir_provider,
            circpool,
            config,
            ipt_watcher,
            config_rx,
            keymgr,
        }
    }

    /// Launch the publisher reactor.
    pub(crate) fn launch(self) -> Result<(), StartupError> {
        let Publisher {
            runtime,
            hsid,
            dir_provider,
            circpool,
            config,
            ipt_watcher,
            config_rx,
            keymgr,
        } = self;

        let state = ReactorState::new(circpool);
        let reactor = Reactor::new(
            runtime.clone(),
            hsid,
            dir_provider,
            state,
            config,
            ipt_watcher,
            config_rx,
            keymgr,
        );

        runtime
            .spawn(async move {
                let _result: Result<(), ReactorError> = reactor.run().await;
            })
            .map_err(|e| StartupError::Spawn {
                spawning: "publisher reactor task",
                cause: e.into(),
            })?;

        Ok(())
    }

    /// Inform this publisher that its set of keys has changed.
    ///
    /// TODO HSS: Either this needs to take new keys as an argument, or there
    /// needs to be a source of keys (including public keys) in Publisher.
    pub(crate) fn new_hs_keys(&self, keys: ()) {
        todo!()
    }

    /// Return our current status.
    //
    // TODO HSS: There should also be a postage::Watcher -based stream of status
    // change events.
    pub(crate) fn status(&self) -> PublisherStatus {
        todo!()
    }

    // TODO HSS: We may also need to update descriptors based on configuration
    // or authentication changes.
}

/// Current status of our attempts to publish an onion service descriptor.
#[derive(Debug, Clone)]
pub(crate) struct PublisherStatus {
    // TODO HSS add fields
}

//
// Our main loop has to look something like:

// Whenever time period or keys or netdir changes: Check whether our list of
// HsDirs has changed.  If it is, add and/or remove hsdirs as needed.

// "when learning about new keys, new intro points, or new configurations,
// or whenever the time period changes: Mark descriptors dirty."

// Whenever descriptors are dirty, we have enough info to generate
// descriptors, and we aren't upload-rate-limited: Generate new descriptors
// and mark descriptors clean.  Mark all hsdirs as needing new versions of
// this descriptor.

// While any hsdir does not have the latest version of its any descriptor:
// upload it.  Retry with usual timeouts on failure."

// TODO HSS: tests