make resolution test work
This commit is contained in:
parent
984a05e873
commit
d13186e404
@ -7,3 +7,6 @@ license = "MIT or Apache 2.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
minijinja = "1.0.12"
|
minijinja = "1.0.12"
|
||||||
tempfile = "3.9.0"
|
tempfile = "3.9.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
doctest = false
|
||||||
|
@ -12,40 +12,26 @@ pub struct AuthoritativeNameServer<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AuthoritativeNameServer<'a> {
|
impl<'a> AuthoritativeNameServer<'a> {
|
||||||
pub fn start(domain: Domain<'a>, referrals: &[Referral<'a>]) -> Result<Self> {
|
/// Spins up a container in a parked state where the name server is not running yet
|
||||||
let container = Container::run()?;
|
pub fn reserve() -> Result<StoppedAuthoritativeNameServer> {
|
||||||
|
|
||||||
// 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 ns_count = crate::nameserver_count();
|
let ns_count = crate::nameserver_count();
|
||||||
let ns = Domain(format!("primary.ns{ns_count}.com."))?;
|
let nameserver = primary_ns(ns_count);
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
container.cp(zone_path, &zone.to_string(), CHMOD_RW_EVERYONE)?;
|
Ok(StoppedAuthoritativeNameServer {
|
||||||
|
container: Container::run()?,
|
||||||
let child = container.spawn(&["nsd", "-d"])?;
|
nameserver,
|
||||||
|
ns_count,
|
||||||
Ok(Self {
|
|
||||||
child,
|
|
||||||
container,
|
|
||||||
zone,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
pub fn ipv4_addr(&self) -> Ipv4Addr {
|
||||||
self.container.ipv4_addr()
|
self.container.ipv4_addr()
|
||||||
}
|
}
|
||||||
@ -53,6 +39,10 @@ impl<'a> AuthoritativeNameServer<'a> {
|
|||||||
pub fn nameserver(&self) -> &Domain<'a> {
|
pub fn nameserver(&self) -> &Domain<'a> {
|
||||||
&self.zone.soa.ns
|
&self.zone.soa.ns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn zone(&self) -> &Zone<'a> {
|
||||||
|
&self.zone
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for AuthoritativeNameServer<'_> {
|
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 {
|
fn nsd_conf(domain: &Domain) -> String {
|
||||||
minijinja::render!(
|
minijinja::render!(
|
||||||
include_str!("templates/nsd.conf.jinja"),
|
include_str!("templates/nsd.conf.jinja"),
|
||||||
@ -74,7 +143,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tld_ns() -> Result<()> {
|
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 ip_addr = tld_ns.ipv4_addr();
|
||||||
|
|
||||||
let client = Container::run()?;
|
let client = Container::run()?;
|
||||||
@ -89,7 +158,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn root_ns() -> Result<()> {
|
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 ip_addr = root_ns.ipv4_addr();
|
||||||
|
|
||||||
let client = Container::run()?;
|
let client = Container::run()?;
|
||||||
@ -112,6 +181,7 @@ mod tests {
|
|||||||
ipv4_addr: expected_ip_addr,
|
ipv4_addr: expected_ip_addr,
|
||||||
ns: Domain("primary.tld-server.com.")?,
|
ns: Domain("primary.tld-server.com.")?,
|
||||||
}],
|
}],
|
||||||
|
&[],
|
||||||
)?;
|
)?;
|
||||||
let ip_addr = root_ns.ipv4_addr();
|
let ip_addr = root_ns.ipv4_addr();
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ impl<'a> Zone<'a> {
|
|||||||
ns: ns.clone(),
|
ns: ns.clone(),
|
||||||
});
|
});
|
||||||
self.record(A {
|
self.record(A {
|
||||||
domain: domain.clone(),
|
domain: ns.clone(),
|
||||||
ipv4_addr: *ipv4_addr,
|
ipv4_addr: *ipv4_addr,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -128,6 +128,7 @@ impl fmt::Display for Record<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct A<'a> {
|
pub struct A<'a> {
|
||||||
pub domain: Domain<'a>,
|
pub domain: Domain<'a>,
|
||||||
pub ipv4_addr: Ipv4Addr,
|
pub ipv4_addr: Ipv4Addr,
|
||||||
|
@ -40,36 +40,94 @@ impl Drop for RecursiveResolver {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{record::Referral, AuthoritativeNameServer, Domain};
|
use crate::{
|
||||||
|
record::{self, Referral},
|
||||||
|
AuthoritativeNameServer, Domain,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_resolve() -> Result<()> {
|
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,
|
Domain::ROOT,
|
||||||
&[Referral {
|
&[Referral {
|
||||||
domain: Domain("com.")?,
|
domain: com_domain,
|
||||||
ipv4_addr: tld_ns.ipv4_addr(),
|
ipv4_addr: com_ns.ipv4_addr(),
|
||||||
ns: tld_ns.nameserver().clone(),
|
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 roots = &[Root::new(root_ns.nameserver().clone(), root_ns.ipv4_addr())];
|
||||||
let resolver = RecursiveResolver::start(roots)?;
|
let resolver = RecursiveResolver::start(roots)?;
|
||||||
let resolver_ip_addr = resolver.ipv4_addr();
|
let resolver_ip_addr = resolver.ipv4_addr();
|
||||||
|
|
||||||
let container = Container::run()?;
|
let container = Container::run()?;
|
||||||
let output =
|
let output = container.output(&[
|
||||||
container.output(&["dig", &format!("@{}", resolver_ip_addr), "example.com"])?;
|
"dig",
|
||||||
|
&format!("@{}", resolver_ip_addr),
|
||||||
|
&needle.to_string(),
|
||||||
|
])?;
|
||||||
|
|
||||||
eprintln!("{}", output.stdout);
|
eprintln!("{}", output.stdout);
|
||||||
|
|
||||||
assert!(output.status.success());
|
assert!(output.status.success());
|
||||||
assert!(output.stdout.contains("status: NOERROR"));
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user