From 1196e1b6800273680a4c893d50ea31e77fd7746c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 26 Sep 2022 09:28:53 -0400 Subject: [PATCH] GuardMgr/DirMgr: Add APIs for bridge descriptor lists --- crates/tor-dirmgr/Cargo.toml | 1 + crates/tor-dirmgr/src/lib.rs | 1 + crates/tor-guardmgr/Cargo.toml | 4 +- crates/tor-guardmgr/src/bridge.rs | 4 +- crates/tor-guardmgr/src/bridge/descs.rs | 97 +++++++++++++++++++++++++ crates/tor-guardmgr/src/lib.rs | 12 +++ 6 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 crates/tor-guardmgr/src/bridge/descs.rs diff --git a/crates/tor-dirmgr/Cargo.toml b/crates/tor-dirmgr/Cargo.toml index f4ff077b6..20baac447 100644 --- a/crates/tor-dirmgr/Cargo.toml +++ b/crates/tor-dirmgr/Cargo.toml @@ -15,6 +15,7 @@ repository = "https://gitlab.torproject.org/tpo/core/arti.git/" default = ["mmap"] full = ["routerdesc"] experimental = ["experimental-api", "dirfilter"] +bridge-client = ["tor-guardmgr/bridge-client", "routerdesc"] mmap = ["memmap2"] static = ["rusqlite/bundled"] diff --git a/crates/tor-dirmgr/src/lib.rs b/crates/tor-dirmgr/src/lib.rs index c4b1bfe35..860294205 100644 --- a/crates/tor-dirmgr/src/lib.rs +++ b/crates/tor-dirmgr/src/lib.rs @@ -153,6 +153,7 @@ pub trait DirProvider: NetDirProvider { // NOTE(eta): We can't implement this for Arc> due to trait coherence rules, so instead // there's a blanket impl for Arc in tor-netdir. +// TODO pt-client: Also implement BridgeDescProvider for DirMgr. impl NetDirProvider for DirMgr { fn netdir(&self, timeliness: Timeliness) -> tor_netdir::Result> { use tor_netdir::Error as NetDirError; diff --git a/crates/tor-guardmgr/Cargo.toml b/crates/tor-guardmgr/Cargo.toml index 57cd52db2..81f22297e 100644 --- a/crates/tor-guardmgr/Cargo.toml +++ b/crates/tor-guardmgr/Cargo.toml @@ -12,12 +12,12 @@ categories = ["network-programming", "cryptography"] repository = "https://gitlab.torproject.org/tpo/core/arti.git/" [features] -default = ["bridge-client"] +default = [] # Support for using bridges as a client. Note that this is not the same as # other crates' pt-client feature, since here we are not concerned with # pluggable transports necessarily. -bridge-client = [] +bridge-client = ["tor-netdoc/routerdesc"] # Enable testing-only APIs. APIs under this feature are not # covered by semver. diff --git a/crates/tor-guardmgr/src/bridge.rs b/crates/tor-guardmgr/src/bridge.rs index ba7226b5c..3a9391ac1 100644 --- a/crates/tor-guardmgr/src/bridge.rs +++ b/crates/tor-guardmgr/src/bridge.rs @@ -8,8 +8,10 @@ //! When a client is configured to use bridges, it uses them in place of its //! regular set of guards in building the first hop of its circuits. // -// TODO pt-client: Put this whole module behind a "bridge" feature? +// TODO pt-client: Should this whole module be in another crate? mod config; +mod descs; pub use config::Bridge; +pub use descs::{BridgeDescEvent, BridgeDescList, BridgeDescProvider}; diff --git a/crates/tor-guardmgr/src/bridge/descs.rs b/crates/tor-guardmgr/src/bridge/descs.rs new file mode 100644 index 000000000..abef22c59 --- /dev/null +++ b/crates/tor-guardmgr/src/bridge/descs.rs @@ -0,0 +1,97 @@ +//! Code for working with bridge descriptors. +//! +//! Here we need to keep track of which bridge descriptors we need, and inform +//! the directory manager of them. + +// TODO pt-client: remove these "allow"s. +#![allow(clippy::missing_panics_doc)] +#![allow(dead_code, unused_variables, clippy::needless_pass_by_value)] + +use std::sync::Arc; + +use futures::stream::BoxStream; +use tor_linkspec::{OwnedChanTarget, RelayId, RelayIds}; + +// TODO pt-client: I think we may want another layer of abstraction between +// RouterDesc and BridgeDesc, to implement e.g. CircTarget for RouterDesc. +// Likely it should contain an Arc. +use tor_netdoc::doc::routerdesc::RouterDesc as BridgeDesc; + +/// This is analogous to NetDirProvider. +/// +/// TODO pt-client: improve documentation. +pub trait BridgeDescProvider { + /// Return the current set of bridge descriptors. + fn bridges(&self) -> Arc; + /// Return a stream that gets a notification when the set of bridge + /// descriptors has changed. + fn events(&self) -> BoxStream<'static, BridgeDescEvent>; + + /// Change the set of bridges that we want to download descriptors for. + /// + /// Bridges outside of this set will not have their descriptors updated, + /// and will not be revealed in the BridgeDescList. + fn set_bridges(&self, bridges: &[OwnedChanTarget]); +} + +/// An event describing a change in a `BridgeDescList`. +#[derive(Debug, Clone)] +#[non_exhaustive] +pub enum BridgeDescEvent { + /// A new descriptor has arrived + // + // TODO: (Should we do anything to indicate which one? If so, we + // won't be able to use a flag-based publisher.) + NewDesc, +} + +/// A set of bridge descriptors, managed and modified by a BridgeDescProvider. +#[derive(Clone, Debug)] +pub struct BridgeDescList { + /// The known bridges. + /// + /// TODO pt-client: This is almost certainly the wrong data structure; some + /// kind of ID-based hashmap is likelier to be right. + /// + /// TODO pt-client: Maybe we should have an intermediary struct between + /// RouterDescriptors and "usable bridge", as we have for `Relay`. + bridges: Vec, +} + +impl BridgeDescList { + /// Return the bridge descriptor, if any, for the given `RelayId`. + pub fn by_id(&self, id: &RelayId) -> Option<&BridgeDesc> { + todo!() // TODO pt-client: implement. + } + + /// Return the bridge descriptor, if any, that has all of the given `RelayIds`. + pub fn by_ids(&self, id: &RelayIds) -> Option<&BridgeDesc> { + todo!() // TODO pt-client: implement. + } + + /// Return an iterator over every bridge descriptor in this list. + /// + /// No bridge descriptors will be returned more than once, and no more than + /// one descriptor will be returned for any given `RelayId`. + pub fn bridges(&self) -> impl Iterator { + todo!(); // TODO pt-client: implement. + #[allow(unreachable_code)] + [].iter() + } + + /// Insert `desc` into this list of bridges. + /// + /// Replace every already-existing descriptor that shares any identity with + /// `desc`. + pub fn insert(&mut self, desc: BridgeDesc) { + todo!() // TODO pt-client: implement. + } + + /// Drop every member of this list for which `func` returns false. + pub fn retain(&mut self, func: F) + where + F: FnMut(&BridgeDesc) -> bool, + { + todo!() // TODO pt-client: implement. + } +} diff --git a/crates/tor-guardmgr/src/lib.rs b/crates/tor-guardmgr/src/lib.rs index b6e774518..3447a7c91 100644 --- a/crates/tor-guardmgr/src/lib.rs +++ b/crates/tor-guardmgr/src/lib.rs @@ -416,6 +416,18 @@ impl GuardMgr { Ok(()) } + /// Configure a new BridgeDescProvider. + /// + /// TODO pt-client: give this more documentation, like install_netdir_provider has. + #[cfg(feature = "bridge-client")] + #[allow(clippy::needless_pass_by_value, clippy::missing_panics_doc)] + pub fn install_bridge_desc_provider( + &self, + _provider: Arc, + ) -> Result<(), GuardMgrError> { + todo!() // TODO pt-client: Implement this and remove the clippy exceptions above. + } + /// Flush our current guard state to the state manager, if there /// is any unsaved state. pub fn store_persistent_state(&self) -> Result<(), GuardMgrError> {