summaryrefslogtreecommitdiff
path: root/crates/tor-hsservice/src/svc/publish.rs
blob: 257c6eb5be8def67131eea5b47f8e04ba30f67bb (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
//! Publish and maintain onion service descriptors

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

mod descriptor;
mod err;
mod reactor;

use futures::channel::mpsc;
use futures::task::SpawnExt;
use std::sync::Arc;
use std::time::Duration;
use tracing::error;

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

use crate::OnionServiceConfig;

pub(crate) use descriptor::Ipt;
use err::PublisherError;
use reactor::{Event, 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.
pub(crate) struct Publisher {
    // TODO HSS: Write the contents here.
    //
    // I'm assuming that each Publisher knows its current keys, keeps track of
    // the current relevant time periods, and knows the current
    // status for uploading to each HsDir.
    //
    // Some of these contents may actually wind up belonging to a reactor
    // task.
    /// A channel for sending `Event`s to the reactor.
    tx: mpsc::UnboundedSender<Event>,
}

/// Set of introduction points to be advertised in a descriptor (if we are to publish)
///
/// If `Some`, the publisher will try to maintain a published descriptor,
/// of lifetime `lifetime`, listing `ipts`.
///
/// If `None`, the publisher will not try to publish.
/// (Already-published descriptors will not be deleted.)
///
/// These instructions ultimately come from
/// [`IptManager::compute_iptsetstatus_publish`](crate::ipt_mgr::IptManager::compute_iptsetstatus_publish).
pub(crate) type PublishIptSet = Option<IptSet>;

/// A set of introduction points for publication
pub(crate) struct IptSet {
    /// The actual introduction points
    pub(crate) ipts: Vec<Ipt>,

    /// When to make the descriptor expire
    pub(crate) lifetime: Duration,
}

impl Publisher {
    /// Create and launch a new publisher.
    ///
    /// When it launches, it will know no keys or introduction points,
    /// and will therefore not upload any descriptors.
    pub(crate) async fn new<R: Runtime>(
        runtime: R,
        hsid: HsId,
        dir_provider: Arc<dyn NetDirProvider>,
        circpool: Arc<HsCircPool<R>>,
        config: OnionServiceConfig,
    ) -> Result<Self, PublisherError> {
        let (tx, rx) = mpsc::unbounded();
        let state = ReactorState::new(circpool);
        let Ok(reactor) =
            Reactor::new(runtime.clone(), hsid, dir_provider, state, config, rx).await
        else {
            error!("failed to create reactor");
            panic!();
        };

        // TODO: Do we really want to launch now, or later?
        runtime
            .spawn(async move {
                let _result: Result<(), ReactorError> = reactor.run().await;
            })
            .map_err(|e| PublisherError::from_spawn("publisher reactor task", e))?;

        Ok(Self { tx })
    }

    /// 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 HSS: handle/return the error
        let _ = self.tx.unbounded_send(Event::NewKeys(()));
    }

    /// Inform this publisher that  the set of introduction points may have changed.
    pub(crate) fn new_intro_points(&self, ipts: PublishIptSet) {
        // TODO HSS: handle/return the error
        let _ = self.tx.unbounded_send(Event::NewIntroPoints(ipts));
    }

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