Commit e0073a8f authored by Nick Mathewson's avatar Nick Mathewson 🥔
Browse files

client: Reflect manually disabled bootstrap in status

If we have made bootstrapping manual, and not launched a bootstrap,
we now reflect this as the reason that bootstrapping is blocked.
parent 16d8c698
Loading
Loading
Loading
Loading
+65 −3
Original line number Diff line number Diff line
@@ -170,9 +170,16 @@ struct ClientShared<R: Runtime> {
    /// mutex used to prevent two tasks from trying to bootstrap at once.
    bootstrap_in_progress: AsyncMutex<()>,

    /// Sender used to update changes in our bootstrap settings.
    bootstrap_setting_sender: Mutex<postage::watch::Sender<BootstrapSetting>>,

    /// Whether or not we should call `bootstrap` before doing things that require
    /// bootstrapping. If this is `false`, we will just call `wait_for_bootstrap`
    /// instead.
    /// bootstrapping.
    ///
    /// If this is [`BootstrapBehavior::OnDemand`], we wait for the client to bootstrap
    /// (launching a bootstrap if necessary) before performing any operation that needs circuits.
    /// If this is [`BootstrapBehavior::Manual`], we give an error if we are told to do
    /// something that needs circuits and we have not been told to bootstrap.
    should_bootstrap: BootstrapBehavior,

    /// Shared boolean for whether we're currently in "dormant mode" or not.
@@ -220,6 +227,9 @@ struct NotConstructedInner<R: Runtime> {
    /// With some redesign we could simplify this, and do away with [`Inner::Poisoned`].
    status_sender: postage::watch::Sender<BootstrapStatus>,

    /// A receiver used to inform the bootstrap status processor about changes in our settings.
    bootstrap_setting_receiver: postage::watch::Receiver<BootstrapSetting>,

    /// A (possibly user-provided) builder used to construct our NetDirProvider.
    dirmgr_builder: Arc<dyn crate::builder::DirProviderBuilder<R>>,

@@ -590,6 +600,41 @@ pub enum BootstrapBehavior {
    Manual,
}

/// A representation of whether a [`TorClient`] is allowed to bootstrap, and whether it
/// has begun to do so.
#[derive(Debug, Clone, Copy)]
pub(crate) struct BootstrapSetting {
    /// The configured [`BootstrapBehavior`] for the `TorClient`.
    behavior: BootstrapBehavior,

    /// If true, we have a [`RunningInner`] in the `TorClient`,
    /// indicating that we are trying to bootstrap it.
    running_inner_is_present: bool,
}

impl Default for BootstrapSetting {
    fn default() -> Self {
        Self {
            behavior: BootstrapBehavior::Manual,
            running_inner_is_present: false,
        }
    }
}

impl BootstrapSetting {
    /// Return true if this [`BootstrapSetting`]
    /// indicates that the client is not trying to bootstrap,
    /// and will not try until it is told explicitly to do so.
    pub(crate) fn blocked(&self) -> bool {
        use BootstrapBehavior::*;
        match (self.behavior, self.running_inner_is_present) {
            (OnDemand, _) => false,
            (Manual, true) => false,
            (Manual, false) => true,
        }
    }
}

/// What level of sleep to put a Tor client into.
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[non_exhaustive]
@@ -963,7 +1008,15 @@ impl<R: Runtime> TorClient<R> {

        let addr_cfg = config.address_filter.clone();

        let (status_sender, status_receiver) = postage::watch::channel();
        let bootstrap_setting = BootstrapSetting {
            behavior: autobootstrap,
            running_inner_is_present: false,
        };
        let (bootstrap_setting_sender, bootstrap_setting_receiver) =
            postage::watch::channel_with(bootstrap_setting);
        let bootstrap_setting_sender = Mutex::new(bootstrap_setting_sender);
        let (status_sender, status_receiver) =
            postage::watch::channel_with(BootstrapStatus::from_setting(bootstrap_setting));
        let status_receiver = status::BootstrapEvents {
            inner: status_receiver,
        };
@@ -981,6 +1034,7 @@ impl<R: Runtime> TorClient<R> {
            config: config.clone(),
            dormant_recv,
            status_sender,
            bootstrap_setting_receiver,
            dirmgr_builder,
            dirmgr_extensions,
        });
@@ -999,6 +1053,7 @@ impl<R: Runtime> TorClient<R> {
            reconfigure_lock: Arc::new(Mutex::new(())),
            status_receiver,
            bootstrap_in_progress: AsyncMutex::new(()),
            bootstrap_setting_sender,
            should_bootstrap: autobootstrap,
            dormant: Mutex::new(dormant_send),
            #[cfg(feature = "onion-service-service")]
@@ -1089,6 +1144,7 @@ impl<R: Runtime> RunningInner<R> {
            config,
            dormant_recv,
            status_sender,
            bootstrap_setting_receiver,
            dirmgr_builder,
            dirmgr_extensions,
        } = pending;
@@ -1237,6 +1293,7 @@ impl<R: Runtime> RunningInner<R> {
                conn_status,
                dir_status,
                skew_status,
                bootstrap_setting_receiver,
            ))
            .map_err(|e| ErrorDetail::from_spawn("top-level status reporter", e))?;

@@ -2218,6 +2275,11 @@ impl<R: Runtime> ClientShared<R> {
                match RunningInner::new(*pending, self) {
                    Ok(running_inner) => {
                        *inner_guard = Inner::Running(Arc::clone(&running_inner));
                        self.bootstrap_setting_sender
                            .lock()
                            .expect("lock poisoned")
                            .borrow_mut()
                            .running_inner_is_present = true;
                        Ok(running_inner)
                    }
                    Err(e) => {
+36 −1
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@ use tor_dirmgr::{DirBlockage, DirBootstrapStatus};
use tracing::debug;
use web_time_compat::{SystemTime, SystemTimeExt};

use crate::client::BootstrapSetting;

/// Information about how ready a [`crate::TorClient`] is to handle requests.
///
/// Note that this status does not change monotonically: a `TorClient` can
@@ -26,6 +28,8 @@ use web_time_compat::{SystemTime, SystemTimeExt};
// its data.
#[derive(Debug, Clone, Default)]
pub struct BootstrapStatus {
    /// Setting for whether we're _trying_ to bootstrap.
    bootstrap_setting: BootstrapSetting,
    /// Status for our connection to the tor network
    conn_status: ConnStatus,
    /// Status for our directory information.
@@ -35,6 +39,14 @@ pub struct BootstrapStatus {
}

impl BootstrapStatus {
    /// Create a new `BootstrapStatus` from a given `BootstrapSetting`
    pub(crate) fn from_setting(bootstrap_setting: BootstrapSetting) -> Self {
        Self {
            bootstrap_setting,
            ..Default::default()
        }
    }

    /// Return a rough fraction (from 0.0 to 1.0) representing how far along
    /// the client's bootstrapping efforts are.
    ///
@@ -73,7 +85,14 @@ impl BootstrapStatus {
    /// can't make connections to the internet" rather than "You are
    /// not on the internet."
    pub fn blocked(&self) -> Option<Blockage> {
        if let Some(b) = self.conn_status.blockage() {
        if self.bootstrap_setting.blocked() {
            Some(Blockage {
                kind: BlockageKind::Disabled,
                message: "Client is waiting to be told to bootstrap"
                    .to_string()
                    .into(),
            })
        } else if let Some(b) = self.conn_status.blockage() {
            let message = b.to_string().into();
            let kind = b.into();
            if matches!(kind, BlockageKind::ClockSkewed) && self.skew_is_noteworthy() {
@@ -109,6 +128,11 @@ impl BootstrapStatus {
        self.skew = status;
    }

    /// Adjust this status based on new bootstrap settings.
    pub(crate) fn apply_bootstrap_setting(&mut self, setting: BootstrapSetting) {
        self.bootstrap_setting = setting;
    }

    /// Return true if our current clock skew estimate is considered noteworthy.
    fn skew_is_noteworthy(&self) -> bool {
        matches!(&self.skew, Some(s) if s.noteworthy())
@@ -143,6 +167,12 @@ impl Blockage {
#[derive(Clone, Debug, derive_more::Display)]
#[non_exhaustive]
pub enum BlockageKind {
    /// The client has been disabled.
    ///
    /// This happens is the client was built with `BootstrapBehavior::Manual`,
    /// and the client has not yet been told that it can bootstrap.
    #[display("Client has been disabled")]
    Disabled,
    /// There is some kind of problem with connecting to the network.
    #[display("We seem to be offline")]
    Offline,
@@ -220,6 +250,7 @@ pub(crate) async fn report_status(
    conn_status: ConnStatusEvents,
    dir_status: impl Stream<Item = DirBootstrapStatus> + Send + Unpin,
    skew_status: ClockSkewEvents,
    setting_status: impl Stream<Item = BootstrapSetting> + Send + Unpin,
) {
    /// Internal enumeration to combine incoming status changes.
    #[allow(clippy::large_enum_variant)]
@@ -230,11 +261,14 @@ pub(crate) async fn report_status(
        Dir(DirBootstrapStatus),
        /// A clock skew change
        Skew(Option<SkewEstimate>),
        /// A change in boostrap settings
        Setting(BootstrapSetting),
    }
    let mut stream = futures::stream::select_all(vec![
        conn_status.map(Event::Conn).boxed(),
        dir_status.map(Event::Dir).boxed(),
        skew_status.map(Event::Skew).boxed(),
        setting_status.map(Event::Setting).boxed(),
    ]);

    while let Some(event) = stream.next().await {
@@ -243,6 +277,7 @@ pub(crate) async fn report_status(
            Event::Conn(e) => b.apply_conn_status(e),
            Event::Dir(e) => b.apply_dir_status(e),
            Event::Skew(e) => b.apply_skew_estimate(e),
            Event::Setting(e) => b.apply_bootstrap_setting(e),
        }
        debug!("{}", *b);
    }