Loading crates/tor-dirmgr/src/config.rs +12 −1 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ use tor_checkable::timed::TimerangeBound; use tor_config::{define_list_builder_accessors, ConfigBuildError}; use tor_guardmgr::fallback::FallbackDirBuilder; use tor_guardmgr::fallback::FallbackListBuilder; use tor_netdoc::doc::netstatus; use tor_netdoc::doc::netstatus::{self, Lifetime}; use derive_builder::Builder; use serde::{Deserialize, Serialize}; Loading Loading @@ -212,6 +212,17 @@ impl DirSkewTolerance { .extend_tolerance(self.post_valid_tolerance) .extend_pre_tolerance(self.pre_valid_tolerance) } /// Return a new consensus [`Lifetime`] that extgends the validity intervals /// of `lifetime` according to this configuration. pub(crate) fn extend_lifetime(&self, lifetime: &Lifetime) -> Lifetime { Lifetime::new( lifetime.valid_after() - self.pre_valid_tolerance, lifetime.fresh_until(), lifetime.valid_until() + self.post_valid_tolerance, ) .expect("Logic error when constructing lifetime") } } /// Configuration type for network directory operations. Loading crates/tor-dirmgr/src/event.rs +66 −23 Original line number Diff line number Diff line Loading @@ -256,8 +256,11 @@ pub(crate) enum DirStatusInner { }, /// We've downloaded a consensus, but we haven't validated it yet. FetchingCerts { /// The lifetime of the consensus. /// The actual declared lifetime of the consensus. lifetime: netstatus::Lifetime, /// The lifetime for which we are willing to use this consensus. (This /// may be broader than `lifetime`.) usable_lifetime: netstatus::Lifetime, /// A fraction (in (numerator,denominator) format) of the certificates /// we have for this consensus. n_certs: (u16, u16), Loading @@ -265,8 +268,11 @@ pub(crate) enum DirStatusInner { /// We've validated a consensus and we're fetching (or have fetched) its /// microdescriptors. Validated { /// The lifetime of the consensus. /// The actual declared lifetime of the consensus. lifetime: netstatus::Lifetime, /// The lifetime for which we are willing to use this consensus. (This /// may be broader than `lifetime`.) usable_lifetime: netstatus::Lifetime, /// A fraction (in (numerator,denominator) form) of the microdescriptors /// that we have for this consensus. n_mds: (u32, u32), Loading Loading @@ -359,7 +365,7 @@ impl DirBootstrapStatus { /// Return true if this status indicates that we have a current usable /// directory. pub fn usable_at(&self, now: SystemTime) -> bool { self.current.usable() && self.current.valid_at(now) self.current.usable() && self.current.okay_to_use_at(now) } /// Update this status by replacing its current status (or its next status) Loading Loading @@ -390,8 +396,8 @@ impl DirBootstrapStatus { } impl DirStatus { /// Return the consensus lifetime for this directory, if we have one. fn lifetime(&self) -> Option<&netstatus::Lifetime> { /// Return the declared consensus lifetime for this directory, if we have one. fn declared_lifetime(&self) -> Option<&netstatus::Lifetime> { match &self.0 { DirStatusInner::NoConsensus { .. } => None, DirStatusInner::FetchingCerts { lifetime, .. } => Some(lifetime), Loading @@ -399,19 +405,41 @@ impl DirStatus { } } /// Return true if the directory is valid at the given time. fn valid_at(&self, when: SystemTime) -> bool { if let Some(lifetime) = self.lifetime() { lifetime.valid_after() <= when && when < lifetime.valid_until() } else { false /// Return the consensus lifetime for this directory, if we have one, as /// modified by our skew-tolerance settings. fn usable_lifetime(&self) -> Option<&netstatus::Lifetime> { match &self.0 { DirStatusInner::NoConsensus { .. } => None, DirStatusInner::FetchingCerts { usable_lifetime, .. } => Some(usable_lifetime), DirStatusInner::Validated { usable_lifetime, .. } => Some(usable_lifetime), } } /// Return true if the directory is valid at the given time, as modified by /// our clock skew settings. fn okay_to_use_at(&self, when: SystemTime) -> bool { self.usable_lifetime() .map(|lt| lt.valid_at(when)) .unwrap_or(false) } /// As frac_at, but return None if this consensus is not valid at the given time. /// As `frac`, but return None if this consensus is not valid at the given time, /// and down-rate expired consensuses that we're still willing to use. fn frac_at(&self, when: SystemTime) -> Option<f32> { if self.valid_at(when) { if self .declared_lifetime() .map(|lt| lt.valid_at(when)) .unwrap_or(false) { // We're officially okay to use this directory. Some(self.frac()) } else if self.okay_to_use_at(when) { // This directory is a little expired, but only a little. Some(self.frac() * 0.9) } else { None } Loading Loading @@ -637,20 +665,28 @@ mod test { let hour = Duration::new(3600, 0); let nothing = DirStatus(DirStatusInner::NoConsensus { after: None }); let lifetime = netstatus::Lifetime::new(now, now + hour, now + hour * 2).unwrap(); let unval = DirStatus(DirStatusInner::FetchingCerts { lifetime: netstatus::Lifetime::new(now, now + hour, now + hour * 2).unwrap(), lifetime: lifetime.clone(), usable_lifetime: lifetime, n_certs: (3, 5), }); let lifetime = netstatus::Lifetime::new(now + hour, now + hour * 2, now + hour * 3).unwrap(); let with_c = DirStatus(DirStatusInner::Validated { lifetime: netstatus::Lifetime::new(now + hour, now + hour * 2, now + hour * 3).unwrap(), lifetime: lifetime.clone(), usable_lifetime: lifetime, n_mds: (30, 40), usable: false, }); // lifetime() assert!(nothing.lifetime().is_none()); assert_eq!(unval.lifetime().unwrap().valid_after(), now); assert_eq!(with_c.lifetime().unwrap().valid_until(), now + hour * 3); assert!(nothing.usable_lifetime().is_none()); assert_eq!(unval.usable_lifetime().unwrap().valid_after(), now); assert_eq!( with_c.usable_lifetime().unwrap().valid_until(), now + hour * 3 ); // at_least_as_new_as() assert!(!nothing.at_least_as_new_as(¬hing)); Loading Loading @@ -690,19 +726,22 @@ mod test { let ds = DirStatus(DirStatusInner::FetchingCerts { lifetime: lifetime.clone(), usable_lifetime: lifetime.clone(), n_certs: (3, 5), }); assert_eq!(ds.to_string(), "fetching authority certificates (3/5)"); let ds = DirStatus(DirStatusInner::Validated { lifetime: lifetime.clone(), usable_lifetime: lifetime.clone(), n_mds: (30, 40), usable: false, }); assert_eq!(ds.to_string(), "fetching microdescriptors (30/40)"); let ds = DirStatus(DirStatusInner::Validated { lifetime, lifetime: lifetime.clone(), usable_lifetime: lifetime, n_mds: (30, 40), usable: true, }); Loading @@ -722,12 +761,14 @@ mod test { let ds1: DirStatus = DirStatusInner::Validated { lifetime: lifetime.clone(), usable_lifetime: lifetime.clone(), n_mds: (3, 40), usable: true, } .into(); let ds2: DirStatus = DirStatusInner::Validated { lifetime: lifetime2.clone(), usable_lifetime: lifetime2.clone(), n_mds: (5, 40), usable: false, } Loading Loading @@ -756,6 +797,7 @@ mod test { let mut bs = bs; let ds3 = DirStatus(DirStatusInner::Validated { lifetime: lifetime2.clone(), usable_lifetime: lifetime2.clone(), n_mds: (10, 40), usable: false, }); Loading @@ -771,13 +813,14 @@ mod test { // Case 2: The new directory _is_ usable and newer. It will replace the old one. let ds4 = DirStatus(DirStatusInner::Validated { lifetime: lifetime2.clone(), usable_lifetime: lifetime2.clone(), n_mds: (20, 40), usable: true, }); bs.update(ds4); assert!(bs.next.as_ref().is_none()); assert_eq!( bs.current.lifetime().unwrap().valid_after(), bs.current.usable_lifetime().unwrap().valid_after(), lifetime2.valid_after() ); Loading @@ -785,15 +828,15 @@ mod test { bs.update(ds1); assert!(bs.next.as_ref().is_none()); assert_ne!( bs.current.lifetime().unwrap().valid_after(), bs.current.usable_lifetime().unwrap().valid_after(), lifetime.valid_after() ); // Case 4: starting with an unusable directory, we always replace. let mut bs = DirBootstrapStatus::default(); assert!(!ds2.usable()); assert!(bs.current.lifetime().is_none()); assert!(bs.current.usable_lifetime().is_none()); bs.update(ds2); assert!(bs.current.lifetime().is_some()); assert!(bs.current.usable_lifetime().is_some()); } } crates/tor-dirmgr/src/state.rs +6 −0 Original line number Diff line number Diff line Loading @@ -441,6 +441,11 @@ impl<R: Runtime> DirState for GetCertsState<R> { let total_certs = n_missing_certs + n_certs; DirStatusInner::FetchingCerts { lifetime: self.consensus_meta.lifetime().clone(), usable_lifetime: self .config .tolerance .extend_lifetime(self.consensus_meta.lifetime()), n_certs: (n_certs as u16, total_certs as u16), } .into() Loading Loading @@ -841,6 +846,7 @@ impl<R: Runtime> DirState for GetMicrodescsState<R> { let n_present = self.n_microdescs - self.partial.n_missing(); DirStatusInner::Validated { lifetime: self.meta.lifetime().clone(), usable_lifetime: self.config.tolerance.extend_lifetime(self.meta.lifetime()), n_mds: (n_present as u32, self.n_microdescs as u32), usable: self.is_ready(Readiness::Usable), } Loading Loading
crates/tor-dirmgr/src/config.rs +12 −1 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ use tor_checkable::timed::TimerangeBound; use tor_config::{define_list_builder_accessors, ConfigBuildError}; use tor_guardmgr::fallback::FallbackDirBuilder; use tor_guardmgr::fallback::FallbackListBuilder; use tor_netdoc::doc::netstatus; use tor_netdoc::doc::netstatus::{self, Lifetime}; use derive_builder::Builder; use serde::{Deserialize, Serialize}; Loading Loading @@ -212,6 +212,17 @@ impl DirSkewTolerance { .extend_tolerance(self.post_valid_tolerance) .extend_pre_tolerance(self.pre_valid_tolerance) } /// Return a new consensus [`Lifetime`] that extgends the validity intervals /// of `lifetime` according to this configuration. pub(crate) fn extend_lifetime(&self, lifetime: &Lifetime) -> Lifetime { Lifetime::new( lifetime.valid_after() - self.pre_valid_tolerance, lifetime.fresh_until(), lifetime.valid_until() + self.post_valid_tolerance, ) .expect("Logic error when constructing lifetime") } } /// Configuration type for network directory operations. Loading
crates/tor-dirmgr/src/event.rs +66 −23 Original line number Diff line number Diff line Loading @@ -256,8 +256,11 @@ pub(crate) enum DirStatusInner { }, /// We've downloaded a consensus, but we haven't validated it yet. FetchingCerts { /// The lifetime of the consensus. /// The actual declared lifetime of the consensus. lifetime: netstatus::Lifetime, /// The lifetime for which we are willing to use this consensus. (This /// may be broader than `lifetime`.) usable_lifetime: netstatus::Lifetime, /// A fraction (in (numerator,denominator) format) of the certificates /// we have for this consensus. n_certs: (u16, u16), Loading @@ -265,8 +268,11 @@ pub(crate) enum DirStatusInner { /// We've validated a consensus and we're fetching (or have fetched) its /// microdescriptors. Validated { /// The lifetime of the consensus. /// The actual declared lifetime of the consensus. lifetime: netstatus::Lifetime, /// The lifetime for which we are willing to use this consensus. (This /// may be broader than `lifetime`.) usable_lifetime: netstatus::Lifetime, /// A fraction (in (numerator,denominator) form) of the microdescriptors /// that we have for this consensus. n_mds: (u32, u32), Loading Loading @@ -359,7 +365,7 @@ impl DirBootstrapStatus { /// Return true if this status indicates that we have a current usable /// directory. pub fn usable_at(&self, now: SystemTime) -> bool { self.current.usable() && self.current.valid_at(now) self.current.usable() && self.current.okay_to_use_at(now) } /// Update this status by replacing its current status (or its next status) Loading Loading @@ -390,8 +396,8 @@ impl DirBootstrapStatus { } impl DirStatus { /// Return the consensus lifetime for this directory, if we have one. fn lifetime(&self) -> Option<&netstatus::Lifetime> { /// Return the declared consensus lifetime for this directory, if we have one. fn declared_lifetime(&self) -> Option<&netstatus::Lifetime> { match &self.0 { DirStatusInner::NoConsensus { .. } => None, DirStatusInner::FetchingCerts { lifetime, .. } => Some(lifetime), Loading @@ -399,19 +405,41 @@ impl DirStatus { } } /// Return true if the directory is valid at the given time. fn valid_at(&self, when: SystemTime) -> bool { if let Some(lifetime) = self.lifetime() { lifetime.valid_after() <= when && when < lifetime.valid_until() } else { false /// Return the consensus lifetime for this directory, if we have one, as /// modified by our skew-tolerance settings. fn usable_lifetime(&self) -> Option<&netstatus::Lifetime> { match &self.0 { DirStatusInner::NoConsensus { .. } => None, DirStatusInner::FetchingCerts { usable_lifetime, .. } => Some(usable_lifetime), DirStatusInner::Validated { usable_lifetime, .. } => Some(usable_lifetime), } } /// Return true if the directory is valid at the given time, as modified by /// our clock skew settings. fn okay_to_use_at(&self, when: SystemTime) -> bool { self.usable_lifetime() .map(|lt| lt.valid_at(when)) .unwrap_or(false) } /// As frac_at, but return None if this consensus is not valid at the given time. /// As `frac`, but return None if this consensus is not valid at the given time, /// and down-rate expired consensuses that we're still willing to use. fn frac_at(&self, when: SystemTime) -> Option<f32> { if self.valid_at(when) { if self .declared_lifetime() .map(|lt| lt.valid_at(when)) .unwrap_or(false) { // We're officially okay to use this directory. Some(self.frac()) } else if self.okay_to_use_at(when) { // This directory is a little expired, but only a little. Some(self.frac() * 0.9) } else { None } Loading Loading @@ -637,20 +665,28 @@ mod test { let hour = Duration::new(3600, 0); let nothing = DirStatus(DirStatusInner::NoConsensus { after: None }); let lifetime = netstatus::Lifetime::new(now, now + hour, now + hour * 2).unwrap(); let unval = DirStatus(DirStatusInner::FetchingCerts { lifetime: netstatus::Lifetime::new(now, now + hour, now + hour * 2).unwrap(), lifetime: lifetime.clone(), usable_lifetime: lifetime, n_certs: (3, 5), }); let lifetime = netstatus::Lifetime::new(now + hour, now + hour * 2, now + hour * 3).unwrap(); let with_c = DirStatus(DirStatusInner::Validated { lifetime: netstatus::Lifetime::new(now + hour, now + hour * 2, now + hour * 3).unwrap(), lifetime: lifetime.clone(), usable_lifetime: lifetime, n_mds: (30, 40), usable: false, }); // lifetime() assert!(nothing.lifetime().is_none()); assert_eq!(unval.lifetime().unwrap().valid_after(), now); assert_eq!(with_c.lifetime().unwrap().valid_until(), now + hour * 3); assert!(nothing.usable_lifetime().is_none()); assert_eq!(unval.usable_lifetime().unwrap().valid_after(), now); assert_eq!( with_c.usable_lifetime().unwrap().valid_until(), now + hour * 3 ); // at_least_as_new_as() assert!(!nothing.at_least_as_new_as(¬hing)); Loading Loading @@ -690,19 +726,22 @@ mod test { let ds = DirStatus(DirStatusInner::FetchingCerts { lifetime: lifetime.clone(), usable_lifetime: lifetime.clone(), n_certs: (3, 5), }); assert_eq!(ds.to_string(), "fetching authority certificates (3/5)"); let ds = DirStatus(DirStatusInner::Validated { lifetime: lifetime.clone(), usable_lifetime: lifetime.clone(), n_mds: (30, 40), usable: false, }); assert_eq!(ds.to_string(), "fetching microdescriptors (30/40)"); let ds = DirStatus(DirStatusInner::Validated { lifetime, lifetime: lifetime.clone(), usable_lifetime: lifetime, n_mds: (30, 40), usable: true, }); Loading @@ -722,12 +761,14 @@ mod test { let ds1: DirStatus = DirStatusInner::Validated { lifetime: lifetime.clone(), usable_lifetime: lifetime.clone(), n_mds: (3, 40), usable: true, } .into(); let ds2: DirStatus = DirStatusInner::Validated { lifetime: lifetime2.clone(), usable_lifetime: lifetime2.clone(), n_mds: (5, 40), usable: false, } Loading Loading @@ -756,6 +797,7 @@ mod test { let mut bs = bs; let ds3 = DirStatus(DirStatusInner::Validated { lifetime: lifetime2.clone(), usable_lifetime: lifetime2.clone(), n_mds: (10, 40), usable: false, }); Loading @@ -771,13 +813,14 @@ mod test { // Case 2: The new directory _is_ usable and newer. It will replace the old one. let ds4 = DirStatus(DirStatusInner::Validated { lifetime: lifetime2.clone(), usable_lifetime: lifetime2.clone(), n_mds: (20, 40), usable: true, }); bs.update(ds4); assert!(bs.next.as_ref().is_none()); assert_eq!( bs.current.lifetime().unwrap().valid_after(), bs.current.usable_lifetime().unwrap().valid_after(), lifetime2.valid_after() ); Loading @@ -785,15 +828,15 @@ mod test { bs.update(ds1); assert!(bs.next.as_ref().is_none()); assert_ne!( bs.current.lifetime().unwrap().valid_after(), bs.current.usable_lifetime().unwrap().valid_after(), lifetime.valid_after() ); // Case 4: starting with an unusable directory, we always replace. let mut bs = DirBootstrapStatus::default(); assert!(!ds2.usable()); assert!(bs.current.lifetime().is_none()); assert!(bs.current.usable_lifetime().is_none()); bs.update(ds2); assert!(bs.current.lifetime().is_some()); assert!(bs.current.usable_lifetime().is_some()); } }
crates/tor-dirmgr/src/state.rs +6 −0 Original line number Diff line number Diff line Loading @@ -441,6 +441,11 @@ impl<R: Runtime> DirState for GetCertsState<R> { let total_certs = n_missing_certs + n_certs; DirStatusInner::FetchingCerts { lifetime: self.consensus_meta.lifetime().clone(), usable_lifetime: self .config .tolerance .extend_lifetime(self.consensus_meta.lifetime()), n_certs: (n_certs as u16, total_certs as u16), } .into() Loading Loading @@ -841,6 +846,7 @@ impl<R: Runtime> DirState for GetMicrodescsState<R> { let n_present = self.n_microdescs - self.partial.n_missing(); DirStatusInner::Validated { lifetime: self.meta.lifetime().clone(), usable_lifetime: self.config.tolerance.extend_lifetime(self.meta.lifetime()), n_mds: (n_present as u32, self.n_microdescs as u32), usable: self.is_ready(Readiness::Usable), } Loading