A little documentation in tor-dirmgr

This commit is contained in:
Nick Mathewson 2020-11-23 09:23:20 -05:00
parent 8a9654ee90
commit b4831da536
1 changed files with 44 additions and 14 deletions

View File

@ -1,5 +1,12 @@
//#![allow(unused_variables)]
//#![allow(unused)]
//! Code to fetch, store, and update directory information.
//!
//! In its current design, Tor requires a set of up-to-date
//! authenticated directory documents in order to build multi-hop
//! anonymized circuits through the network.
//!
//! This directory manager crate is responsible for figuring out which
//! directory information we lack, downloading what we're missing, and
//! keeping a cache of it on disk.
pub mod authority;
// TODO: make this private.
@ -30,13 +37,22 @@ pub use authority::Authority;
pub use config::{NetDirConfig, NetDirConfigBuilder};
pub use err::Error;
/// A directory manager to download, fetch, and cache a Tor directory
pub struct DirMgr {
/// Configuration information: where to find directories, how to
/// validate them, and so on.
config: NetDirConfig,
/// Handle to our sqlite cache.
store: RwLock<SqliteStore>,
/// Our latest sufficiently bootstrapped directory, if we have one.
///
/// We use the RwLock so that we can give this out to a bunch of other
/// users, and replace it once a new directory is bootstrapped.
netdir: RwLock<Option<Arc<NetDir>>>,
}
impl DirMgr {
/// Construct a DirMgr from a NetDirConfig.
pub fn from_config(config: NetDirConfig) -> Result<Self> {
let store = RwLock::new(config.open_sqlite_store()?);
let netdir = RwLock::new(None);
@ -47,6 +63,11 @@ impl DirMgr {
})
}
/// Run a complete bootstrapping process, using information from our
/// cache when it is up-to-date enough. When complete, update our
/// NetDir with the one we've fetched.
///
// TODO: We'll likely need to refactor this before too long.
pub async fn bootstrap_directory<TR>(&self, circmgr: Arc<CircMgr<TR>>) -> Result<()>
where
TR: tor_chanmgr::transport::Transport,
@ -64,7 +85,7 @@ impl DirMgr {
// TODO: need to make consensus non-pending eventually.
let mut unval = match noinfo.load(true, store).await? {
NextState::NoChange(noinfo) => {
NextState::SameState(noinfo) => {
noinfo
.fetch_consensus(store, dirinfo, Arc::clone(&circmgr))
.await?
@ -78,7 +99,7 @@ impl DirMgr {
.await?;
let mut partial = match unval.advance()? {
// TODO: retry.
NextState::NoChange(_) => return Err(anyhow!("Couldn't get certs")),
NextState::SameState(_) => return Err(anyhow!("Couldn't get certs")),
NextState::NewState(p) => p,
};
@ -90,7 +111,7 @@ impl DirMgr {
let nd = match partial.advance() {
// XXXX Retry.
NextState::NewState(nd) => nd,
NextState::NoChange(_) => return Err(anyhow!("Didn't get enough mds")),
NextState::SameState(_) => return Err(anyhow!("Didn't get enough mds")),
};
{
@ -101,28 +122,37 @@ impl DirMgr {
Ok(())
}
/// Return an Arc handle to our latest sufficiently up-to-date directory.
///
// TODO: make sure it's still up to date?
pub async fn netdir(&self) -> Option<Arc<NetDir>> {
self.netdir.read().await.as_ref().map(Arc::clone)
}
}
/// Abstraction to handle the idea of a possible state transition
/// after fetching or loading directory information.
#[derive(Clone, Debug)]
pub enum NextState<A, B>
enum NextState<A, B>
where
A: Clone + Debug,
B: Clone + Debug,
{
NoChange(A),
/// We either got no new info, or we didn't get enough info to update
/// to a new state.
SameState(A),
/// We found enough information to transition to a new state.
NewState(B),
}
/// Initial directory state when no information is known.
#[derive(Debug, Clone, Default)]
pub struct NoInformation {
struct NoInformation {
authorities: Vec<Authority>,
}
#[derive(Debug, Clone)]
pub struct UnvalidatedDir {
struct UnvalidatedDir {
from_cache: bool,
authorities: Vec<Authority>,
consensus: UnvalidatedMDConsensus,
@ -130,7 +160,7 @@ pub struct UnvalidatedDir {
}
#[derive(Debug, Clone)]
pub struct PartialDir {
struct PartialDir {
from_cache: bool,
dir: PartialNetDir,
}
@ -150,7 +180,7 @@ impl NoInformation {
let store = store.read().await;
match store.latest_consensus(pending)? {
Some(c) => c,
None => return Ok(NextState::NoChange(self)),
None => return Ok(NextState::SameState(self)),
}
};
let unvalidated = {
@ -159,7 +189,7 @@ impl NoInformation {
if let Ok(timely) = parsed.check_valid_now() {
timely
} else {
return Ok(NextState::NoChange(self));
return Ok(NextState::SameState(self));
}
};
let n_authorities = self.authorities.len() as u16;
@ -317,7 +347,7 @@ impl UnvalidatedDir {
dir: PartialNetDir::new(validated),
}))
} else {
Ok(NextState::NoChange(self))
Ok(NextState::SameState(self))
}
}
}
@ -350,7 +380,7 @@ impl PartialDir {
fn advance(self) -> NextState<Self, NetDir> {
match self.dir.unwrap_if_sufficient() {
Ok(netdir) => NextState::NewState(netdir),
Err(partial) => NextState::NoChange(PartialDir {
Err(partial) => NextState::SameState(PartialDir {
from_cache: self.from_cache,
dir: partial,
}),