Commit c0399fc6 authored by Nick Mathewson's avatar Nick Mathewson 🦀
Browse files

dirmgr: Initial DirFilter code.

This code sits behind a feature flag, and can be used to modify
directories before storing them.  This is part of the implementation
for #397.
parent 43544159
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -300,6 +300,7 @@ impl TryInto<dir::DirMgrConfig> for &TorClientConfig {
            schedule_config:     self.download_schedule  .clone(),
            cache_path:          self.storage.expand_cache_dir()?,
            override_net_params: self.override_net_params.clone(),
            extensions:          Default::default(),
        })
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ mmap = ["memmap2"]
static = ["rusqlite/bundled"]
# (Incomplete) support for downloading and storing router descriptors
routerdesc = ["tor-dirclient/routerdesc"]
dirfilter = []

# Enable experimental APIs that are not yet officially supported.
#
+16 −0
Original line number Diff line number Diff line
@@ -199,6 +199,12 @@ pub struct DirMgrConfig {
    /// immediately. Users should _not_ assume that the effect of changing this
    /// option will always be delayed.)
    pub override_net_params: netstatus::NetParams<i32>,

    /// Extra fields for extension purposes.
    ///
    /// These are kept in a separate type so that the type can be marked as
    /// `non_exhaustive` and used for optional features.
    pub extensions: DirMgrExtensions,
}

impl DirMgrConfig {
@@ -252,6 +258,7 @@ impl DirMgrConfig {
            },
            schedule_config: new_config.schedule_config.clone(),
            override_net_params: new_config.override_net_params.clone(),
            extensions: new_config.extensions.clone(),
        }
    }

@@ -265,6 +272,15 @@ impl DirMgrConfig {
    }
}

/// Optional extensions for configuring
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct DirMgrExtensions {
    /// A filter to be used when installing new directory objects.
    #[cfg(feature = "dirfilter")]
    pub filter: Option<crate::filter::DynFilter>,
}

impl DownloadScheduleConfig {
    /// Return configuration for retrying our entire bootstrap
    /// operation at startup.
+83 −0
Original line number Diff line number Diff line
//! A filtering mechanism for directory objects.
//!
//! This module and its members are only available when `tor-dirmgr` is built
//! with the `dirfilter` feature.
//!
//! This is unstable code, currently used for testing only.  It might go away in
//! future versions, or its API might change completely. There are no semver
//! guarantees.

use std::sync::Arc;

use crate::Result;
use tor_netdoc::doc::{microdesc::Microdesc, netstatus::UncheckedMdConsensus};

/// An object that can filter directory documents before they're handled.
///
/// Instances of DirFilter can be used for testing, to modify directory data
/// on-the-fly.
pub trait DirFilter {
    /// Modify `consensus` in an unspecified way.
    fn filter_consensus(&self, consensus: UncheckedMdConsensus) -> Result<UncheckedMdConsensus>;
    /// Modify `md` in an unspecified way.
    fn filter_md(&self, md: Microdesc) -> Result<Microdesc>;
}

/// A dynamic [`DirFilter`] instance.
#[derive(Clone)]
pub struct DynFilter {
    /// A reference to the DirFilter object
    filter: Arc<dyn DirFilter + Send + Sync>,
}

impl From<&Option<DynFilter>> for DynFilter {
    fn from(option: &Option<DynFilter>) -> Self {
        option.as_ref().map(Clone::clone).unwrap_or_default()
    }
}

impl Default for DynFilter {
    fn default() -> Self {
        DynFilter::new(NilFilter)
    }
}

impl DynFilter {
    /// Wrap `filter` as a [`DynFilter`]
    pub fn new<T>(filter: T) -> Self
    where
        T: DirFilter + Send + Sync + 'static,
    {
        DynFilter {
            filter: Arc::new(filter),
        }
    }
}

impl DirFilter for DynFilter {
    fn filter_consensus(&self, consensus: UncheckedMdConsensus) -> Result<UncheckedMdConsensus> {
        self.filter.filter_consensus(consensus)
    }

    fn filter_md(&self, md: Microdesc) -> Result<Microdesc> {
        self.filter.filter_md(md)
    }
}

impl std::fmt::Debug for DynFilter {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("DynFilter").finish_non_exhaustive()
    }
}

/// A [`DirFilter`] that does nothing.
struct NilFilter;

impl DirFilter for NilFilter {
    fn filter_consensus(&self, consensus: UncheckedMdConsensus) -> Result<UncheckedMdConsensus> {
        Ok(consensus)
    }
    fn filter_md(&self, md: Microdesc) -> Result<Microdesc> {
        Ok(md)
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -66,6 +66,9 @@ mod shared_ref;
mod state;
mod storage;

#[cfg(feature = "dirfilter")]
pub mod filter;

use crate::docid::{CacheUsage, ClientRequest, DocQuery};
#[cfg(not(feature = "experimental-api"))]
use crate::shared_ref::SharedMutArc;
@@ -222,6 +225,10 @@ pub struct DirMgr<R: Runtime> {
    ///
    /// (In offline mode, this does nothing.)
    bootstrap_started: AtomicBool,

    /// A filter that gets applied to directory objects before we use them.
    #[cfg(feature = "dirfilter")]
    filter: filter::DynFilter,
}

/// RAII guard to reset an AtomicBool on drop.
@@ -702,6 +709,8 @@ impl<R: Runtime> DirMgr<R> {
        let receive_status = DirBootstrapEvents {
            inner: receive_status,
        };
        #[cfg(feature = "dirfilter")]
        let filter = (&config.extensions.filter).into();

        Ok(DirMgr {
            config: config.into(),
@@ -714,6 +723,8 @@ impl<R: Runtime> DirMgr<R> {
            runtime,
            offline,
            bootstrap_started: AtomicBool::new(false),
            #[cfg(feature = "dirfilter")]
            filter,
        })
    }

Loading