#![allow(clippy::missing_panics_doc)]
#![allow(clippy::unwrap_used)]
use crate::{MdDigest, MdReceiver, PartialNetDir};
use std::iter;
use std::net::SocketAddr;
use std::time::{Duration, SystemTime};
use tor_netdoc::doc::microdesc::{Microdesc, MicrodescBuilder};
use tor_netdoc::doc::netstatus::MdConsensus;
use tor_netdoc::doc::netstatus::{Lifetime, RelayFlags, RelayWeight, RouterStatusBuilder};
pub use tor_netdoc::{BuildError, BuildResult};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct NodeBuilders {
pub rs: RouterStatusBuilder<MdDigest>,
pub md: MicrodescBuilder,
pub omit_md: bool,
pub omit_rs: bool,
}
fn simple_net_func(_idx: usize, _nb: &mut NodeBuilders) {}
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,
) -> BuildResult<PartialNetDir>
where
F: FnMut(usize, &mut NodeBuilders),
P: IntoIterator<Item = (PK, i32)>,
PK: Into<String>,
{
let (consensus, microdescs) = construct_custom_network(func)?;
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),
{
construct_custom_netdir_with_params(func, iter::empty::<(&str, _)>())
}
pub fn construct_network() -> BuildResult<(MdConsensus, Vec<Microdesc>)> {
construct_custom_network(simple_net_func)
}
pub fn construct_custom_network<F>(mut func: F) -> BuildResult<(MdConsensus, Vec<Microdesc>)>
where
F: FnMut(usize, &mut NodeBuilders),
{
let f = RelayFlags::RUNNING | RelayFlags::VALID | RelayFlags::V2DIR;
let flags = [
f,
f | RelayFlags::EXIT,
f | RelayFlags::GUARD,
f | RelayFlags::EXIT | RelayFlags::GUARD,
];
let now = SystemTime::now();
let one_day = Duration::new(86400, 0);
let mut bld = MdConsensus::builder();
bld.consensus_method(34)
.lifetime(Lifetime::new(now, now + one_day / 2, now + one_day)?)
.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(RelayFlags::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);
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 {
use super::*;
#[test]
fn try_with_function() {
let mut val = 0_u32;
let _net = construct_custom_netdir(|_idx, _nb| {
val += 1;
});
assert_eq!(val, 40);
}
}