Merge pull request #65 from ferrous-systems/ja-graph-two-point-o
tweak `Graph::build` to support anydomain.com. as leaf zone
This commit is contained in:
commit
568c75ec6f
@ -3,9 +3,9 @@ use std::net::Ipv4Addr;
|
|||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use dns_test::client::Client;
|
use dns_test::client::Client;
|
||||||
use dns_test::name_server::NameServer;
|
use dns_test::name_server::{Graph, NameServer, Sign};
|
||||||
use dns_test::record::RecordType;
|
use dns_test::record::RecordType;
|
||||||
use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN};
|
use dns_test::{Network, Resolver, Result, FQDN};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let args = Args::from_env()?;
|
let args = Args::from_env()?;
|
||||||
@ -14,49 +14,16 @@ fn main() -> Result<()> {
|
|||||||
let peer = &dns_test::PEER;
|
let peer = &dns_test::PEER;
|
||||||
|
|
||||||
println!("building docker image...");
|
println!("building docker image...");
|
||||||
let mut root_ns = NameServer::new(peer, FQDN::ROOT, &network)?;
|
let leaf_ns = NameServer::new(peer, FQDN("mydomain.com.")?, &network)?;
|
||||||
println!("DONE");
|
println!("DONE");
|
||||||
|
|
||||||
println!("setting up name servers...");
|
println!("setting up name servers...");
|
||||||
let mut com_ns = NameServer::new(peer, FQDN::COM, &network)?;
|
let sign = if args.dnssec { Sign::Yes } else { Sign::No };
|
||||||
|
let Graph {
|
||||||
let mut nameservers_ns = NameServer::new(peer, FQDN("nameservers.com.")?, &network)?;
|
root,
|
||||||
nameservers_ns.add(root_ns.a()).add(com_ns.a());
|
trust_anchor,
|
||||||
|
nameservers,
|
||||||
let nameservers_ns = if args.dnssec {
|
} = Graph::build(leaf_ns, sign)?;
|
||||||
let nameservers_ns = nameservers_ns.sign()?;
|
|
||||||
com_ns.add(nameservers_ns.ds().clone());
|
|
||||||
nameservers_ns.start()?
|
|
||||||
} else {
|
|
||||||
nameservers_ns.start()?
|
|
||||||
};
|
|
||||||
|
|
||||||
com_ns.referral_nameserver(&nameservers_ns);
|
|
||||||
|
|
||||||
let com_ns = if args.dnssec {
|
|
||||||
let com_ns = com_ns.sign()?;
|
|
||||||
root_ns.add(com_ns.ds().clone());
|
|
||||||
com_ns.start()?
|
|
||||||
} else {
|
|
||||||
com_ns.start()?
|
|
||||||
};
|
|
||||||
|
|
||||||
root_ns.referral_nameserver(&com_ns);
|
|
||||||
|
|
||||||
let mut trust_anchor = TrustAnchor::empty();
|
|
||||||
let root_ns = if args.dnssec {
|
|
||||||
let root_ns = root_ns.sign()?;
|
|
||||||
let root_ksk = root_ns.key_signing_key();
|
|
||||||
let root_zsk = root_ns.zone_signing_key();
|
|
||||||
|
|
||||||
trust_anchor.add(root_ksk.clone());
|
|
||||||
trust_anchor.add(root_zsk.clone());
|
|
||||||
|
|
||||||
root_ns.start()?
|
|
||||||
} else {
|
|
||||||
root_ns.start()?
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("DONE");
|
println!("DONE");
|
||||||
|
|
||||||
let client = Client::new(&network)?;
|
let client = Client::new(&network)?;
|
||||||
@ -68,40 +35,29 @@ fn main() -> Result<()> {
|
|||||||
Ipv4Addr::new(127, 0, 0, 1),
|
Ipv4Addr::new(127, 0, 0, 1),
|
||||||
RecordType::SOA,
|
RecordType::SOA,
|
||||||
&FQDN::ROOT,
|
&FQDN::ROOT,
|
||||||
&trust_anchor,
|
trust_anchor.as_ref().unwrap(),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("building docker image...");
|
println!("building docker image...");
|
||||||
let resolver = Resolver::new(&network, root_ns.root_hint())
|
let mut builder = Resolver::new(&network, root);
|
||||||
.trust_anchor(&trust_anchor)
|
if let Some(trust_anchor) = trust_anchor {
|
||||||
.start(&dns_test::SUBJECT)?;
|
builder.trust_anchor(&trust_anchor);
|
||||||
|
}
|
||||||
|
let resolver = builder.start(&dns_test::SUBJECT)?;
|
||||||
println!("DONE\n\n");
|
println!("DONE\n\n");
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
ctrlc::set_handler(move || tx.send(()).expect("could not forward signal"))?;
|
ctrlc::set_handler(move || tx.send(()).expect("could not forward signal"))?;
|
||||||
|
|
||||||
println!(". (root) name server's IP address: {}", root_ns.ipv4_addr());
|
for ns in &nameservers {
|
||||||
println!(
|
println!("{} name server's IP address: {}", ns.zone(), ns.ipv4_addr());
|
||||||
"attach to this container with: `docker exec -it {} bash`\n",
|
println!(
|
||||||
root_ns.container_id()
|
"attach to this container with: `docker exec -it {} bash`\n",
|
||||||
);
|
ns.container_id()
|
||||||
|
);
|
||||||
println!("com. name server's IP address: {}", com_ns.ipv4_addr());
|
}
|
||||||
println!(
|
|
||||||
"attach to this container with: `docker exec -it {} bash`\n",
|
|
||||||
com_ns.container_id()
|
|
||||||
);
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"nameservers.com. name server's IP address: {}",
|
|
||||||
nameservers_ns.ipv4_addr()
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"attach to this container with: `docker exec -it {} bash`\n",
|
|
||||||
nameservers_ns.container_id()
|
|
||||||
);
|
|
||||||
|
|
||||||
let resolver_addr = resolver.ipv4_addr();
|
let resolver_addr = resolver.ipv4_addr();
|
||||||
println!("resolver's IP address: {resolver_addr}",);
|
println!("resolver's IP address: {resolver_addr}",);
|
||||||
|
@ -19,7 +19,6 @@ pub enum Sign<'a> {
|
|||||||
No,
|
No,
|
||||||
Yes,
|
Yes,
|
||||||
/// Signs the zone files and then modifies the records produced by the signing process
|
/// Signs the zone files and then modifies the records produced by the signing process
|
||||||
// XXX if captures are needed use `&dyn Fn(..)` instead of a function pointer
|
|
||||||
AndAmend(&'a dyn Fn(&FQDN, &mut Vec<Record>)),
|
AndAmend(&'a dyn Fn(&FQDN, &mut Vec<Record>)),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,46 +35,54 @@ impl Graph {
|
|||||||
///
|
///
|
||||||
/// a non-empty `TrustAnchor` is returned only when `Sign::Yes` or `Sign::AndAmend` is used
|
/// a non-empty `TrustAnchor` is returned only when `Sign::Yes` or `Sign::AndAmend` is used
|
||||||
pub fn build(leaf: NameServer<Stopped>, sign: Sign) -> Result<Self> {
|
pub fn build(leaf: NameServer<Stopped>, sign: Sign) -> Result<Self> {
|
||||||
// TODO if `leaf` is not authoritative over `nameservers.com.`, we would need two "lines" to
|
assert_eq!(2, leaf.zone().num_labels(), "not yet implemented");
|
||||||
// root. for example, if `leaf` is authoritative over `example.net.` we would need these two
|
assert_eq!(Some(FQDN::COM), leaf.zone().parent(), "not yet implemented");
|
||||||
// lines:
|
|
||||||
// - `nameservers.com.`, `com.`, `.` to cover the `primaryNNN.nameservers.com.` domains that
|
|
||||||
// `NameServer` implicitly uses
|
|
||||||
// - `example.net.`, `net.`, `.` to cover the requested `leaf` name server
|
|
||||||
assert_eq!(&FQDN::NAMESERVERS, leaf.zone(), "not yet implemented");
|
|
||||||
|
|
||||||
// first pass: create nameservers for parent zones
|
// first pass: create nameservers for parent zones
|
||||||
let mut zone = leaf.zone().clone();
|
let mut zone = leaf.zone().clone();
|
||||||
let mut nameservers = vec![leaf];
|
let network = leaf.container.network().clone();
|
||||||
while let Some(parent) = zone.parent() {
|
let implementation = leaf.implementation.clone();
|
||||||
let leaf = &mut nameservers[0];
|
|
||||||
let nameserver = NameServer::new(
|
|
||||||
&leaf.implementation,
|
|
||||||
parent.clone(),
|
|
||||||
leaf.container.network(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
leaf.add(nameserver.a());
|
let (mut nameservers_ns, leaf) = if leaf.zone() != &FQDN::NAMESERVERS {
|
||||||
|
let nameservers_ns = NameServer::new(&implementation, FQDN::NAMESERVERS, &network)?;
|
||||||
|
(nameservers_ns, Some(leaf))
|
||||||
|
} else {
|
||||||
|
(leaf, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
// the nameserver covering `FQDN::NAMESERVERS` needs A records about all the nameservers in the graph
|
||||||
|
let mut nameservers = vec![];
|
||||||
|
while let Some(parent) = zone.parent() {
|
||||||
|
let nameserver = NameServer::new(&implementation, parent.clone(), &network)?;
|
||||||
|
|
||||||
|
nameservers_ns.add(nameserver.a());
|
||||||
nameservers.push(nameserver);
|
nameservers.push(nameserver);
|
||||||
|
|
||||||
zone = parent;
|
zone = parent;
|
||||||
}
|
}
|
||||||
|
drop((network, implementation));
|
||||||
|
|
||||||
// XXX will not hold when `leaf` is not authoritative over `nameservers.com.`
|
if let Some(leaf) = leaf {
|
||||||
assert_eq!(3, nameservers.len());
|
nameservers.insert(0, leaf);
|
||||||
|
}
|
||||||
|
nameservers.insert(0, nameservers_ns);
|
||||||
|
|
||||||
// second pass: add referrals from parent to child
|
// second pass: add referrals from parent to child
|
||||||
// `windows_mut` is not a thing in `core::iter` so use indexing as a workaround
|
// the nameservers are sorted leaf-most zone first but siblings may be next to each other
|
||||||
for index in 0..nameservers.len() - 1 {
|
// for each child (e.g. `nameservers.com.`), do a linear search for its parent (`com.`)
|
||||||
let [child, parent] = &mut nameservers[index..][..2] else {
|
for index in 1..nameservers.len() {
|
||||||
unreachable!()
|
let (left, right) = nameservers.split_at_mut(index);
|
||||||
};
|
let child = left.last_mut().unwrap();
|
||||||
|
for maybe_parent in right {
|
||||||
parent.referral_nameserver(child);
|
if Some(maybe_parent.zone()) == child.zone().parent().as_ref() {
|
||||||
|
let parent = maybe_parent;
|
||||||
|
parent.referral_nameserver(child);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let root = nameservers.last().unwrap();
|
let root = nameservers.last().unwrap().root_hint();
|
||||||
let root = Root::new(root.fqdn().clone(), root.ipv4_addr());
|
|
||||||
|
|
||||||
// start name servers
|
// start name servers
|
||||||
let (nameservers, trust_anchor) = match sign {
|
let (nameservers, trust_anchor) = match sign {
|
||||||
@ -96,15 +103,22 @@ impl Graph {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut running = vec![];
|
let mut running = vec![];
|
||||||
let mut child_ds = None;
|
let mut children_ds = vec![];
|
||||||
|
let mut children_num_labels = 0;
|
||||||
let len = nameservers.len();
|
let len = nameservers.len();
|
||||||
for (index, mut nameserver) in nameservers.into_iter().enumerate() {
|
for (index, mut nameserver) in nameservers.into_iter().enumerate() {
|
||||||
if let Some(ds) = child_ds.take() {
|
if !children_ds.is_empty() {
|
||||||
nameserver.add(ds);
|
let is_parent = nameserver.zone().num_labels() + 1 == children_num_labels;
|
||||||
|
if is_parent {
|
||||||
|
for ds in children_ds.drain(..) {
|
||||||
|
nameserver.add(ds);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut nameserver = nameserver.sign()?;
|
let mut nameserver = nameserver.sign()?;
|
||||||
child_ds = Some(nameserver.ds().clone());
|
children_ds.push(nameserver.ds().clone());
|
||||||
|
children_num_labels = nameserver.zone().num_labels();
|
||||||
if let Some(mutate) = maybe_mutate {
|
if let Some(mutate) = maybe_mutate {
|
||||||
let zone = nameserver.zone().clone();
|
let zone = nameserver.zone().clone();
|
||||||
mutate(&zone, &mut nameserver.signed_zone_file_mut().records);
|
mutate(&zone, &mut nameserver.signed_zone_file_mut().records);
|
||||||
@ -153,7 +167,7 @@ impl NameServer<Stopped> {
|
|||||||
/// the zone
|
/// the zone
|
||||||
pub fn new(implementation: &Implementation, zone: FQDN, network: &Network) -> Result<Self> {
|
pub fn new(implementation: &Implementation, zone: FQDN, network: &Network) -> Result<Self> {
|
||||||
let ns_count = ns_count();
|
let ns_count = ns_count();
|
||||||
let nameserver = primary_ns(ns_count);
|
let nameserver = primary_ns(ns_count, &zone);
|
||||||
let image = implementation.clone().into();
|
let image = implementation.clone().into();
|
||||||
let container = Container::run(&image, network)?;
|
let container = Container::run(&image, network)?;
|
||||||
|
|
||||||
@ -161,7 +175,7 @@ impl NameServer<Stopped> {
|
|||||||
zone: zone.clone(),
|
zone: zone.clone(),
|
||||||
ttl: DEFAULT_TTL,
|
ttl: DEFAULT_TTL,
|
||||||
nameserver: nameserver.clone(),
|
nameserver: nameserver.clone(),
|
||||||
admin: admin_ns(ns_count),
|
admin: admin_ns(ns_count, &zone),
|
||||||
settings: SoaSettings::default(),
|
settings: SoaSettings::default(),
|
||||||
};
|
};
|
||||||
let mut zone_file = ZoneFile::new(soa);
|
let mut zone_file = ZoneFile::new(soa);
|
||||||
@ -439,12 +453,22 @@ pub struct Running {
|
|||||||
child: Child,
|
child: Child,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn primary_ns(ns_count: usize) -> FQDN {
|
fn primary_ns(ns_count: usize, zone: &FQDN) -> FQDN {
|
||||||
FQDN(format!("primary{ns_count}.nameservers.com.")).unwrap()
|
FQDN(format!("primary{ns_count}.{}", expand_zone(zone))).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn admin_ns(ns_count: usize) -> FQDN {
|
fn admin_ns(ns_count: usize, zone: &FQDN) -> FQDN {
|
||||||
FQDN(format!("admin{ns_count}.nameservers.com.")).unwrap()
|
FQDN(format!("admin{ns_count}.{}", expand_zone(zone))).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_zone(zone: &FQDN) -> String {
|
||||||
|
if zone == &FQDN::ROOT {
|
||||||
|
"nameservers.com.".to_string()
|
||||||
|
} else if zone.num_labels() == 1 {
|
||||||
|
format!("nameservers.{}", zone.as_str())
|
||||||
|
} else {
|
||||||
|
zone.to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
Loading…
Reference in New Issue
Block a user