#![allow(clippy::unwrap_used)]
use crate::{MdReceiver, PartialNetDir};
use std::iter;
use std::net::SocketAddr;
#[cfg(feature = "geoip")]
use tor_geoip::GeoipDb;
use tor_netdoc::doc::microdesc::{Microdesc, MicrodescBuilder};
use tor_netdoc::doc::netstatus::{Lifetime, MdRouterStatusBuilder, RelayWeight};
use tor_netdoc::doc::netstatus::{MdConsensus, MdConsensusBuilder};
use tor_netdoc::types::relay_flags::RelayFlag;
pub use tor_netdoc::{BuildError, BuildResult};
use web_time_compat::{Duration, SystemTime, SystemTimeExt};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct NodeBuilders {
pub rs: MdRouterStatusBuilder,
pub md: MicrodescBuilder,
pub omit_md: bool,
pub omit_rs: bool,
}
pub fn simple_net_func(_idx: usize, _nb: &mut NodeBuilders, _bld: &mut MdConsensusBuilder) {}
pub fn construct_netdir() -> PartialNetDir {
construct_custom_netdir(simple_net_func).expect("failed to build default testing netdir")
}
pub fn construct_custom_netdir_with_params<F, P, PK>(
func: F,
params: P,
lifetime: Option<Lifetime>,
) -> BuildResult<PartialNetDir>
where
F: FnMut(usize, &mut NodeBuilders, &mut MdConsensusBuilder),
P: IntoIterator<Item = (PK, i32)>,
PK: Into<String>,
{
construct_custom_netdir_with_params_inner(
func,
params,
lifetime,
#[cfg(feature = "geoip")]
None,
)
}
fn construct_custom_netdir_with_params_inner<F, P, PK>(
func: F,
params: P,
lifetime: Option<Lifetime>,
#[cfg(feature = "geoip")] geoip_db: Option<&GeoipDb>,
) -> BuildResult<PartialNetDir>
where
F: FnMut(usize, &mut NodeBuilders, &mut MdConsensusBuilder),
P: IntoIterator<Item = (PK, i32)>,
PK: Into<String>,
{
let (consensus, microdescs) = construct_custom_network(func, lifetime)?;
#[cfg(feature = "geoip")]
let mut dir = if let Some(db) = geoip_db {
PartialNetDir::new_with_geoip(consensus, Some(¶ms.into_iter().collect()), db)
} else {
PartialNetDir::new(consensus, Some(¶ms.into_iter().collect()))
};
#[cfg(not(feature = "geoip"))]
let mut dir = PartialNetDir::new(consensus, Some(¶ms.into_iter().collect()));
for md in microdescs {
dir.add_microdesc(md);
}
Ok(dir)
}
pub fn construct_custom_netdir<F>(func: F) -> BuildResult<PartialNetDir>
where
F: FnMut(usize, &mut NodeBuilders, &mut MdConsensusBuilder),
{
construct_custom_netdir_with_params(func, iter::empty::<(&str, _)>(), None)
}
#[cfg(feature = "geoip")]
pub fn construct_custom_netdir_with_geoip<F>(func: F, db: &GeoipDb) -> BuildResult<PartialNetDir>
where
F: FnMut(usize, &mut NodeBuilders, &mut MdConsensusBuilder),
{
construct_custom_netdir_with_params_inner(func, iter::empty::<(&str, _)>(), None, Some(db))
}
pub fn construct_network() -> BuildResult<(MdConsensus, Vec<Microdesc>)> {
construct_custom_network(simple_net_func, None)
}
pub fn construct_custom_network<F>(
mut func: F,
lifetime: Option<Lifetime>,
) -> BuildResult<(MdConsensus, Vec<Microdesc>)>
where
F: FnMut(usize, &mut NodeBuilders, &mut MdConsensusBuilder),
{
let f = RelayFlag::Running
| RelayFlag::Valid
| RelayFlag::V2Dir
| RelayFlag::Fast
| RelayFlag::Stable;
let flags = [
f | RelayFlag::HSDir,
f | RelayFlag::Exit,
f | RelayFlag::Guard,
f | RelayFlag::Exit | RelayFlag::Guard,
];
let lifetime = lifetime.map(Ok).unwrap_or_else(|| {
let now = SystemTime::get();
let one_day = Duration::new(86400, 0);
Lifetime::new(now, now + one_day / 2, now + one_day)
})?;
let mut bld = MdConsensus::builder();
bld.consensus_method(34)
.lifetime(lifetime)
.param("bwweightscale", 1)
.weights("".parse()?);
let mut microdescs = Vec::new();
for idx in 0..40_u8 {
let flags = flags[(idx / 10) as usize];
let policy = if flags.contains(RelayFlag::Exit) {
if idx % 2 == 1 {
"accept 80,443"
} else {
"accept 1-65535"
}
} else {
"reject 1-65535"
};
let fam_id = [idx ^ 1; 20];
let family = hex::encode(fam_id);
let mut md_builder = Microdesc::builder();
md_builder
.ntor_key((*b"----nothing in dirmgr uses this-").into())
.ed25519_id([idx; 32].into())
.family(family.parse().unwrap())
.parse_ipv4_policy(policy)
.unwrap();
let protocols = if idx % 2 == 0 {
"DirCache=2".parse().unwrap()
} else {
"".parse().unwrap()
};
let weight = RelayWeight::Measured(1000 * u32::from(idx % 10 + 1));
let mut rs_builder = bld.rs();
rs_builder
.identity([idx; 20].into())
.add_or_port(SocketAddr::from(([idx % 5, 0, 0, 3], 9001)))
.protos(protocols)
.set_flags(flags)
.weight(weight);
let mut node_builders = NodeBuilders {
rs: rs_builder,
md: md_builder,
omit_rs: false,
omit_md: false,
};
func(idx as usize, &mut node_builders, &mut bld);
let md = node_builders.md.testing_md()?;
let md_digest = *md.digest();
if !node_builders.omit_md {
microdescs.push(md);
}
if !node_builders.omit_rs {
node_builders
.rs
.doc_digest(md_digest)
.build_into(&mut bld)?;
}
}
let consensus = bld.testing_consensus()?;
Ok((consensus, microdescs))
}
#[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)]
use super::*;
#[test]
fn try_with_function() {
let mut val = 0_u32;
let _net = construct_custom_netdir(|_idx, _nb, _bld| {
val += 1;
});
assert_eq!(val, 40);
}
}