use std::time::Duration;
use derive_deftly::Deftly;
use getset::{CopyGetters, Getters};
use tor_checkable::timed::TimerangeBound;
use tor_config::derive::prelude::*;
use tor_config::{ConfigBuildError, define_list_builder_accessors};
use tor_netdoc::doc::netstatus::Lifetime;
use crate::{
authority::{AuthorityContacts, AuthorityContactsBuilder},
fallback::{FallbackDirBuilder, FallbackList, FallbackListBuilder},
retry::{DownloadSchedule, DownloadScheduleBuilder},
};
#[derive(Debug, Clone, Deftly, Eq, PartialEq, Getters)]
#[derive_deftly(TorConfig)]
#[deftly(tor_config(pre_build = "Self::validate"))]
#[non_exhaustive]
pub struct NetworkConfig {
#[deftly(tor_config(sub_builder, setter(skip)))]
#[getset(get = "pub")]
fallback_caches: FallbackList,
#[deftly(tor_config(sub_builder))]
#[getset(get = "pub")]
authorities: AuthorityContacts,
}
define_list_builder_accessors! {
struct NetworkConfigBuilder {
pub fallback_caches: [FallbackDirBuilder],
}
}
impl NetworkConfigBuilder {
fn validate(&self) -> std::result::Result<(), ConfigBuildError> {
if self.authorities.opt_v3idents().is_some() && self.opt_fallback_caches().is_none() {
return Err(ConfigBuildError::Inconsistent {
fields: vec!["authorities".to_owned(), "fallbacks".to_owned()],
problem: "Non-default authorities are use, but the fallback list is not overridden"
.to_owned(),
});
}
Ok(())
}
}
#[derive(Debug, Clone, Deftly, Eq, PartialEq, Getters, CopyGetters)]
#[derive_deftly(TorConfig)]
#[non_exhaustive]
pub struct DownloadScheduleConfig {
#[deftly(tor_config(sub_builder(build_fn = "build_retry_bootstrap")))]
#[getset(get_copy = "pub")]
retry_bootstrap: DownloadSchedule,
#[deftly(tor_config(sub_builder))]
#[getset(get_copy = "pub")]
retry_consensus: DownloadSchedule,
#[deftly(tor_config(sub_builder))]
#[getset(get_copy = "pub")]
retry_certs: DownloadSchedule,
#[deftly(tor_config(sub_builder(build_fn = "build_retry_microdescs")))]
#[getset(get_copy = "pub")]
retry_microdescs: DownloadSchedule,
}
#[derive(Debug, Clone, Deftly, Eq, PartialEq, Getters, CopyGetters)]
#[derive_deftly(TorConfig)]
#[non_exhaustive]
pub struct DirTolerance {
#[deftly(tor_config(default = "Duration::from_secs(24 * 60 * 60)"))]
#[getset(get_copy = "pub")]
pre_valid_tolerance: Duration,
#[deftly(tor_config(default = "Duration::from_secs(3 * 24 * 60 * 60)"))]
#[getset(get_copy = "pub")]
post_valid_tolerance: Duration,
}
impl DirTolerance {
pub fn extend_tolerance<B>(&self, timebound: TimerangeBound<B>) -> TimerangeBound<B> {
timebound
.extend_tolerance(self.post_valid_tolerance)
.extend_pre_tolerance(self.pre_valid_tolerance)
}
pub 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")
}
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
#![allow(clippy::unnecessary_wraps)]
use tor_llcrypto::pk::rsa::RsaIdentity;
use crate::fallback::FallbackDir;
use super::*;
#[test]
fn build_network() {
let dflt = NetworkConfig::default();
let mut bld = NetworkConfig::builder();
let cfg = bld.build().unwrap();
assert_eq!(
cfg.authorities.v3idents().len(),
dflt.authorities.v3idents().len()
);
assert_eq!(cfg.fallback_caches.len(), dflt.fallback_caches.len());
bld.authorities
.set_v3idents(vec![[b'?'; 20].into(), [b'!'; 20].into()]);
assert!(bld.build().is_err());
bld.set_fallback_caches(vec![{
let mut bld = FallbackDir::builder();
bld.rsa_identity([b'x'; 20].into())
.ed_identity([b'y'; 32].into());
bld.orports().push("127.0.0.1:99".parse().unwrap());
bld.orports().push("[::]:99".parse().unwrap());
bld
}]);
let cfg = bld.build().unwrap();
assert_eq!(cfg.authorities.v3idents().len(), 2);
assert_eq!(cfg.fallback_caches.len(), 1);
}
#[test]
fn deserialize() {
let mut netcfg_prop330: NetworkConfigBuilder = toml::from_str(
"
[authorities]
v3idents = [
\"911F7C74212214823DDBDE3044B5B1AF3EFB98A0\",
\"46C4A4492D103A8C5CA544AC653B51C7B9AC8692\",
\"28D4680EA9C3660D1028FC40BACAC1319414581E\",
\"3817C9EB7E41C957594D0D9BCD6C7D7D718479C2\",
]",
)
.unwrap();
assert_eq!(netcfg_prop330.authorities.v3idents().len(), 4);
assert_eq!(
*netcfg_prop330.authorities.v3idents(),
vec![
RsaIdentity::from_hex("911F7C74212214823DDBDE3044B5B1AF3EFB98A0").unwrap(),
RsaIdentity::from_hex("46C4A4492D103A8C5CA544AC653B51C7B9AC8692").unwrap(),
RsaIdentity::from_hex("28D4680EA9C3660D1028FC40BACAC1319414581E").unwrap(),
RsaIdentity::from_hex("3817C9EB7E41C957594D0D9BCD6C7D7D718479C2").unwrap(),
]
);
}
}