DirMgr: make DocSource useful by having it include dirserver info.

Previously DocSource would tell you whether the document was from
a local store or a cache server, but it wouldn't tell you _which_
server it came from.

This change required adding DocSource as an argument to
DirState::add_from_download.
This commit is contained in:
Nick Mathewson 2022-05-12 10:54:30 -04:00
parent 18d7ece7dd
commit ef2640acfa
3 changed files with 47 additions and 22 deletions

View File

@ -10,6 +10,7 @@ use std::{
use crate::state::DirState;
use crate::DirMgrConfig;
use crate::DocSource;
use crate::{
docid::{self, ClientRequest},
upgrade_weak_ref, DirMgr, DocId, DocQuery, DocumentText, Error, Readiness, Result,
@ -383,7 +384,11 @@ async fn download_attempt<R: Runtime>(
};
match dirmgr.expand_response_text(&client_req, text) {
Ok(text) => {
let outcome = state.add_from_download(&text, &client_req, Some(&dirmgr.store));
let doc_source = DocSource::DirServer {
source: source.clone(),
};
let outcome =
state.add_from_download(&text, &client_req, doc_source, Some(&dirmgr.store));
match outcome {
Ok(b) => {
changed |= b;
@ -685,6 +690,7 @@ mod test {
&mut self,
text: &str,
_request: &ClientRequest,
_source: DocSource,
_storage: Option<&Mutex<DynStore>>,
) -> Result<bool> {
let mut changed = false;

View File

@ -78,6 +78,7 @@ use crate::storage::{DynStore, Store};
use postage::watch;
pub use retry::{DownloadSchedule, DownloadScheduleBuilder};
use tor_circmgr::CircMgr;
use tor_dirclient::SourceInfo;
use tor_netdir::{DirEvent, MdReceiver, NetDir, NetDirProvider};
use async_trait::async_trait;
@ -261,11 +262,11 @@ pub enum DocSource {
#[display(fmt = "local cache")]
LocalCache,
/// We fetched the document from a server.
//
// TODO: we'll should add a lot more information here in the future, once
// it's available from tor_dirclient::DirSource,
#[display(fmt = "directory server")]
DirServer {},
DirServer {
/// Information about the server we fetched the document from.
source: Option<SourceInfo>,
},
}
impl<R: Runtime> DirMgr<R> {

View File

@ -118,13 +118,11 @@ pub(crate) trait DirState: Send {
/// into `storage` so they can be saved in a cache.
// TODO: It might be good to say "there was a change but also an
// error" in this API if possible.
// TODO: It would be better to not have this function be async,
// once the `must_not_suspend` lint is stable.
// TODO: this should take a "DirSource" too.
fn add_from_download(
&mut self,
text: &str,
request: &ClientRequest,
source: DocSource,
storage: Option<&Mutex<DynStore>>,
) -> Result<bool>;
/// Return a summary of this state as a [`DirStatus`].
@ -286,9 +284,9 @@ impl<R: Runtime> DirState for GetConsensusState<R> {
&mut self,
text: &str,
_request: &ClientRequest,
source: DocSource,
storage: Option<&Mutex<DynStore>>,
) -> Result<bool> {
let source = DocSource::DirServer {};
if let Some(meta) = self.add_consensus_text(source, text)? {
if let Some(store) = storage {
let mut w = store.lock().expect("Directory storage lock poisoned");
@ -477,6 +475,7 @@ impl<R: Runtime> DirState for GetCertsState<R> {
&mut self,
text: &str,
request: &ClientRequest,
source: DocSource,
storage: Option<&Mutex<DynStore>>,
) -> Result<bool> {
let asked_for: HashSet<_> = match request {
@ -496,12 +495,16 @@ impl<R: Runtime> DirState for GetCertsState<R> {
let timely = wellsigned.check_valid_at(&now)?;
newcerts.push((timely, s));
} else {
// TODO: note the source.
warn!("Badly signed certificate received and discarded.");
warn!(
"Badly signed certificate received from {} and discarded.",
source
);
}
} else {
// TODO: note the source.
warn!("Unparsable certificate received and discarded.");
warn!(
"Unparsable certificate received received from {} and discarded.",
source
);
}
}
@ -509,7 +512,10 @@ impl<R: Runtime> DirState for GetCertsState<R> {
let len_orig = newcerts.len();
newcerts.retain(|(cert, _)| asked_for.contains(cert.key_ids()));
if newcerts.len() != len_orig {
warn!("Discarding certificates that we didn't ask for.");
warn!(
"Discarding certificates from {} that we didn't ask for.",
source
);
}
// We want to exit early if we aren't saving any certificates.
@ -879,6 +885,7 @@ impl<R: Runtime> DirState for GetMicrodescsState<R> {
&mut self,
text: &str,
request: &ClientRequest,
source: DocSource,
storage: Option<&Mutex<DynStore>>,
) -> Result<bool> {
let requested: HashSet<_> = if let ClientRequest::Microdescs(req) = request {
@ -894,7 +901,8 @@ impl<R: Runtime> DirState for GetMicrodescsState<R> {
let md = anno.into_microdesc();
if !requested.contains(md.digest()) {
warn!(
"Received microdescriptor we did not ask for: {:?}",
"Received microdescriptor from {} we did not ask for: {:?}",
source,
md.digest()
);
continue;
@ -1190,11 +1198,17 @@ mod test {
cache_usage: CacheUsage::CacheOkay,
}
));
let source = DocSource::DirServer { source: None };
// Now suppose that we get some complete junk from a download.
let req = tor_dirclient::request::ConsensusRequest::new(ConsensusFlavor::Microdesc);
let req = crate::docid::ClientRequest::Consensus(req);
let outcome = state.add_from_download("this isn't a consensus", &req, Some(&store));
let outcome = state.add_from_download(
"this isn't a consensus",
&req,
source.clone(),
Some(&store),
);
assert!(matches!(outcome, Err(Error::NetDocError { .. })));
// make sure it wasn't stored...
assert!(store
@ -1205,7 +1219,7 @@ mod test {
.is_none());
// Now try again, with a real consensus... but the wrong authorities.
let outcome = state.add_from_download(CONSENSUS, &req, Some(&store));
let outcome = state.add_from_download(CONSENSUS, &req, source.clone(), Some(&store));
assert!(matches!(outcome, Err(Error::UnrecognizedAuthorities)));
assert!(store
.lock()
@ -1226,7 +1240,7 @@ mod test {
#[cfg(feature = "dirfilter")]
Arc::new(crate::filter::NilFilter),
);
let outcome = state.add_from_download(CONSENSUS, &req, Some(&store));
let outcome = state.add_from_download(CONSENSUS, &req, source, Some(&store));
assert!(outcome.unwrap());
assert!(store
.lock()
@ -1278,9 +1292,10 @@ mod test {
#[cfg(feature = "dirfilter")]
Arc::new(crate::filter::NilFilter),
);
let source = DocSource::DirServer { source: None };
let req = tor_dirclient::request::ConsensusRequest::new(ConsensusFlavor::Microdesc);
let req = crate::docid::ClientRequest::Consensus(req);
let outcome = state.add_from_download(CONSENSUS, &req, None);
let outcome = state.add_from_download(CONSENSUS, &req, source, None);
assert!(outcome.unwrap());
Box::new(state).advance().unwrap()
}
@ -1337,10 +1352,12 @@ mod test {
// Now try to add the other from a download ... but fail
// because we didn't ask for it.
let source = DocSource::DirServer { source: None };
let mut req = tor_dirclient::request::AuthCertRequest::new();
req.push(authcert_id_5696()); // it's the wrong id.
let req = ClientRequest::AuthCert(req);
let outcome = state.add_from_download(AUTHCERT_5A23, &req, Some(&store));
let outcome =
state.add_from_download(AUTHCERT_5A23, &req, source.clone(), Some(&store));
assert!(!outcome.unwrap()); // no error, but nothing changed.
let missing2 = state.missing_docs();
assert_eq!(missing, missing2); // No change.
@ -1355,7 +1372,7 @@ mod test {
let mut req = tor_dirclient::request::AuthCertRequest::new();
req.push(authcert_id_5a23()); // Right idea this time!
let req = ClientRequest::AuthCert(req);
let outcome = state.add_from_download(AUTHCERT_5A23, &req, Some(&store));
let outcome = state.add_from_download(AUTHCERT_5A23, &req, source, Some(&store));
assert!(outcome.unwrap()); // No error, _and_ something changed!
let missing3 = state.missing_docs();
assert!(missing3.is_empty());
@ -1481,7 +1498,8 @@ mod test {
req.push(md_digest);
}
let req = ClientRequest::Microdescs(req);
let outcome = state.add_from_download(response.as_str(), &req, Some(&store));
let source = DocSource::DirServer { source: None };
let outcome = state.add_from_download(response.as_str(), &req, source, Some(&store));
assert!(outcome.unwrap()); // successfully loaded MDs
match state.get_netdir_change().unwrap() {
NetDirChange::AttemptReplace { netdir, .. } => {