diff --git a/crates/tor-guardmgr/Cargo.toml b/crates/tor-guardmgr/Cargo.toml index 5cb1ac5b2..a606491fd 100644 --- a/crates/tor-guardmgr/Cargo.toml +++ b/crates/tor-guardmgr/Cargo.toml @@ -39,6 +39,7 @@ tor-error = { path = "../tor-error", version = "0.3.1" } tor-linkspec = { path = "../tor-linkspec", version = "0.3.0" } tor-llcrypto = { path = "../tor-llcrypto", version = "0.3.0" } tor-netdir = { path = "../tor-netdir", version = "0.3.0" } +tor-netdoc = { path = "../tor-netdoc", version = "0.4.0" } # for address pattern tor-persist = { path = "../tor-persist", version = "0.4.0" } tor-proto = { path = "../tor-proto", version = "0.3.1" } tor-rtcompat = { path = "../tor-rtcompat", version = "0.4.0" } diff --git a/crates/tor-guardmgr/src/filter.rs b/crates/tor-guardmgr/src/filter.rs index 0f2f18f42..2c1673123 100644 --- a/crates/tor-guardmgr/src/filter.rs +++ b/crates/tor-guardmgr/src/filter.rs @@ -1,6 +1,9 @@ //! Implement GuardFilter and related types. use tor_linkspec::ChanTarget; +// TODO(nickm): Conceivably, this type should be exposed from a lower-level crate than +// tor-netdoc. +use tor_netdoc::types::policy::AddrPortPattern; /// An object specifying which relays are eligible to be guards. /// @@ -20,12 +23,21 @@ pub struct GuardFilter { /// A list of filters to apply to guard or fallback selection. Each filter /// restricts which guards may be used, and possibly how those guards may be /// contacted. + /// + /// This list of filters has "and" semantics: a relay is permitted by this + /// filter if ALL patterns in this list permit that first hop. filters: Vec, } /// A single restriction places upon usable guards. #[derive(Debug, Clone)] enum SingleFilter { + /// A set of allowable addresses that we are willing to try to connect to. + /// + /// This list of patterns has "or" semantics: a guard is permitted by this filter + /// if ANY pattern in this list permits one of the guard's addresses. + ReachableAddrs(Vec), + /// Testing only: checks whether the first byte of the rsa key is 0 modulo 4. /// /// TODO: remove this once real filters are implemented. @@ -41,6 +53,13 @@ impl GuardFilter { GuardFilter::default() } + /// Restrict this filter to only permit connections to an address permitted + /// by one of the patterns in `addrs`. + pub fn push_reachable_addresses(&mut self, addrs: impl IntoIterator) { + self.filters + .push(SingleFilter::ReachableAddrs(addrs.into_iter().collect())); + } + /// Return true if this filter permits the provided `target`. pub(crate) fn permits(&self, target: &C) -> bool { self.filters.iter().all(|filt| filt.permits(target)) @@ -64,16 +83,12 @@ impl GuardFilter { impl SingleFilter { /// Return true if this filter permits the provided target. fn permits(&self, target: &C) -> bool { - // TODO: This is ugly, but the whole function will get rewritten once we - // have a real filter. - #[cfg(test)] match self { + SingleFilter::ReachableAddrs(patterns) => patterns + .iter() + .any(|pat| target.addrs().iter().any(|addr| pat.matches_sockaddr(addr))), + #[cfg(test)] SingleFilter::TestingLimitKeys => target.rsa_identity().as_bytes()[0] & 3 == 0, } - #[cfg(not(test))] - { - let _ = target; - true - } } }