make resolution test work

This commit is contained in:
Jorge Aparicio 2024-02-05 19:21:52 +01:00
parent 984a05e873
commit d13186e404
4 changed files with 172 additions and 40 deletions

View File

@ -7,3 +7,6 @@ license = "MIT or Apache 2.0"
[dependencies]
minijinja = "1.0.12"
tempfile = "3.9.0"
[lib]
doctest = false

View File

@ -12,40 +12,26 @@ pub struct AuthoritativeNameServer<'a> {
}
impl<'a> AuthoritativeNameServer<'a> {
pub fn start(domain: Domain<'a>, referrals: &[Referral<'a>]) -> Result<Self> {
let container = Container::run()?;
// for PID file
container.status_ok(&["mkdir", "-p", "/run/nsd/"])?;
container.status_ok(&["mkdir", "-p", "/etc/nsd/zones"])?;
let zone_path = "/etc/nsd/zones/main.zone";
container.cp("/etc/nsd/nsd.conf", &nsd_conf(&domain), CHMOD_RW_EVERYONE)?;
/// Spins up a container in a parked state where the name server is not running yet
pub fn reserve() -> Result<StoppedAuthoritativeNameServer> {
let ns_count = crate::nameserver_count();
let ns = Domain(format!("primary.ns{ns_count}.com."))?;
let soa = record::Soa {
domain: domain.clone(),
ns,
admin: Domain(format!("admin.ns{ns_count}.com."))?,
settings: SoaSettings::default(),
};
let mut zone = Zone::new(domain, soa);
for referral in referrals {
zone.referral(referral)
}
let nameserver = primary_ns(ns_count);
container.cp(zone_path, &zone.to_string(), CHMOD_RW_EVERYONE)?;
let child = container.spawn(&["nsd", "-d"])?;
Ok(Self {
child,
container,
zone,
Ok(StoppedAuthoritativeNameServer {
container: Container::run()?,
nameserver,
ns_count,
})
}
pub fn start(
domain: Domain<'a>,
referrals: &[Referral<'a>],
a_records: &[record::A<'a>],
) -> Result<Self> {
Self::reserve()?.start(domain, referrals, a_records)
}
pub fn ipv4_addr(&self) -> Ipv4Addr {
self.container.ipv4_addr()
}
@ -53,6 +39,10 @@ impl<'a> AuthoritativeNameServer<'a> {
pub fn nameserver(&self) -> &Domain<'a> {
&self.zone.soa.ns
}
pub fn zone(&self) -> &Zone<'a> {
&self.zone
}
}
impl Drop for AuthoritativeNameServer<'_> {
@ -61,6 +51,85 @@ impl Drop for AuthoritativeNameServer<'_> {
}
}
fn primary_ns(ns_count: usize) -> Domain<'static> {
Domain(format!("primary{ns_count}.nameservers.com.")).unwrap()
}
fn admin_ns(ns_count: usize) -> Domain<'static> {
Domain(format!("admin{ns_count}.nameservers.com.")).unwrap()
}
pub struct StoppedAuthoritativeNameServer {
container: Container,
nameserver: Domain<'static>,
ns_count: usize,
}
impl StoppedAuthoritativeNameServer {
pub fn ipv4_addr(&self) -> Ipv4Addr {
self.container.ipv4_addr()
}
pub fn nameserver(&self) -> &Domain<'static> {
&self.nameserver
}
pub fn start<'a>(
self,
domain: Domain<'a>,
referrals: &[Referral<'a>],
a_records: &[record::A<'a>],
) -> Result<AuthoritativeNameServer<'a>> {
let Self {
container,
nameserver,
ns_count,
} = self;
// for PID file
container.status_ok(&["mkdir", "-p", "/run/nsd/"])?;
container.status_ok(&["mkdir", "-p", "/etc/nsd/zones"])?;
let zone_path = "/etc/nsd/zones/main.zone";
container.cp("/etc/nsd/nsd.conf", &nsd_conf(&domain), CHMOD_RW_EVERYONE)?;
let soa = record::Soa {
domain: domain.clone(),
ns: nameserver.clone(),
admin: admin_ns(ns_count),
settings: SoaSettings::default(),
};
let mut zone = Zone::new(domain.clone(), soa);
zone.record(record::Ns {
domain: domain.clone(),
ns: nameserver,
});
zone.record(record::A {
domain,
ipv4_addr: container.ipv4_addr(),
});
for referral in referrals {
zone.referral(referral)
}
for a in a_records {
zone.record(a.clone())
}
container.cp(zone_path, &zone.to_string(), CHMOD_RW_EVERYONE)?;
let child = container.spawn(&["nsd", "-d"])?;
Ok(AuthoritativeNameServer {
child,
container,
zone,
})
}
}
fn nsd_conf(domain: &Domain) -> String {
minijinja::render!(
include_str!("templates/nsd.conf.jinja"),
@ -74,7 +143,7 @@ mod tests {
#[test]
fn tld_ns() -> Result<()> {
let tld_ns = AuthoritativeNameServer::start(Domain("com.")?, &[])?;
let tld_ns = AuthoritativeNameServer::start(Domain("com.")?, &[], &[])?;
let ip_addr = tld_ns.ipv4_addr();
let client = Container::run()?;
@ -89,7 +158,7 @@ mod tests {
#[test]
fn root_ns() -> Result<()> {
let root_ns = AuthoritativeNameServer::start(Domain::ROOT, &[])?;
let root_ns = AuthoritativeNameServer::start(Domain::ROOT, &[], &[])?;
let ip_addr = root_ns.ipv4_addr();
let client = Container::run()?;
@ -112,6 +181,7 @@ mod tests {
ipv4_addr: expected_ip_addr,
ns: Domain("primary.tld-server.com.")?,
}],
&[],
)?;
let ip_addr = root_ns.ipv4_addr();

View File

@ -43,7 +43,7 @@ impl<'a> Zone<'a> {
ns: ns.clone(),
});
self.record(A {
domain: domain.clone(),
domain: ns.clone(),
ipv4_addr: *ipv4_addr,
});
}
@ -128,6 +128,7 @@ impl fmt::Display for Record<'_> {
}
}
#[derive(Clone)]
pub struct A<'a> {
pub domain: Domain<'a>,
pub ipv4_addr: Ipv4Addr,

View File

@ -40,36 +40,94 @@ impl Drop for RecursiveResolver {
#[cfg(test)]
mod tests {
use crate::{record::Referral, AuthoritativeNameServer, Domain};
use crate::{
record::{self, Referral},
AuthoritativeNameServer, Domain,
};
use super::*;
#[test]
fn can_resolve() -> Result<()> {
let tld_ns = AuthoritativeNameServer::start(Domain("com.")?, &[])?;
let expected_ipv4_addr = Ipv4Addr::new(1, 2, 3, 4);
let needle = Domain("example.nameservers.com.")?;
let root_ns = AuthoritativeNameServer::reserve()?;
let com_ns = AuthoritativeNameServer::reserve()?;
let root_ns = AuthoritativeNameServer::start(
let nameservers_domain = Domain("nameservers.com.")?;
let nameservers_ns = AuthoritativeNameServer::start(
nameservers_domain.clone(),
&[],
&[
record::A {
domain: root_ns.nameserver().clone(),
ipv4_addr: root_ns.ipv4_addr(),
},
record::A {
domain: com_ns.nameserver().clone(),
ipv4_addr: com_ns.ipv4_addr(),
},
record::A {
domain: needle.clone(),
ipv4_addr: expected_ipv4_addr,
},
],
)?;
eprintln!("nameservers.com.zone:\n{}", nameservers_ns.zone());
let com_domain = Domain("com.")?;
let com_ns = com_ns.start(
com_domain.clone(),
&[Referral {
domain: nameservers_domain,
ipv4_addr: nameservers_ns.ipv4_addr(),
ns: nameservers_ns.nameserver().clone(),
}],
&[],
)?;
eprintln!("com.zone:\n{}", com_ns.zone());
let root_ns = root_ns.start(
Domain::ROOT,
&[Referral {
domain: Domain("com.")?,
ipv4_addr: tld_ns.ipv4_addr(),
ns: tld_ns.nameserver().clone(),
domain: com_domain,
ipv4_addr: com_ns.ipv4_addr(),
ns: com_ns.nameserver().clone(),
}],
&[],
)?;
eprintln!("root.zone:\n{}", root_ns.zone());
let roots = &[Root::new(root_ns.nameserver().clone(), root_ns.ipv4_addr())];
let resolver = RecursiveResolver::start(roots)?;
let resolver_ip_addr = resolver.ipv4_addr();
let container = Container::run()?;
let output =
container.output(&["dig", &format!("@{}", resolver_ip_addr), "example.com"])?;
let output = container.output(&[
"dig",
&format!("@{}", resolver_ip_addr),
&needle.to_string(),
])?;
eprintln!("{}", output.stdout);
assert!(output.status.success());
assert!(output.stdout.contains("status: NOERROR"));
let mut found = false;
let needle = needle.to_string();
for line in output.stdout.lines() {
if line.starts_with(&needle) {
found = true;
assert!(line.ends_with(&expected_ipv4_addr.to_string()));
}
}
assert!(found);
Ok(())
}
}