Merge pull request #49 from ferrous-systems/ja-resolver-builder-take-2
refactor: use builder pattern in Resolver ctor
This commit is contained in:
commit
75de211a06
|
@ -4,7 +4,7 @@ use dns_test::client::{Client, DigSettings};
|
|||
use dns_test::name_server::NameServer;
|
||||
use dns_test::record::{Record, RecordType};
|
||||
use dns_test::zone_file::Root;
|
||||
use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN};
|
||||
use dns_test::{Network, Resolver, Result, FQDN};
|
||||
|
||||
#[test]
|
||||
fn can_resolve() -> Result<()> {
|
||||
|
@ -39,8 +39,11 @@ fn can_resolve() -> Result<()> {
|
|||
|
||||
eprintln!("root.zone:\n{}", root_ns.zone_file());
|
||||
|
||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
||||
let resolver = Resolver::start(&dns_test::subject(), roots, &TrustAnchor::empty(), &network)?;
|
||||
let resolver = Resolver::new(
|
||||
&network,
|
||||
Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr()),
|
||||
)
|
||||
.start(&dns_test::subject())?;
|
||||
let resolver_ip_addr = resolver.ipv4_addr();
|
||||
|
||||
let client = Client::new(&network)?;
|
||||
|
@ -85,8 +88,11 @@ fn nxdomain() -> Result<()> {
|
|||
root_ns.referral(FQDN::COM, com_ns.fqdn().clone(), com_ns.ipv4_addr());
|
||||
let root_ns = root_ns.start()?;
|
||||
|
||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
||||
let resolver = Resolver::start(&dns_test::subject(), roots, &TrustAnchor::empty(), &network)?;
|
||||
let resolver = Resolver::new(
|
||||
&network,
|
||||
Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr()),
|
||||
)
|
||||
.start(&dns_test::subject())?;
|
||||
let resolver_ip_addr = resolver.ipv4_addr();
|
||||
|
||||
let client = Client::new(&network)?;
|
||||
|
|
|
@ -3,19 +3,15 @@ use dns_test::name_server::NameServer;
|
|||
use dns_test::record::RecordType;
|
||||
use dns_test::tshark::{Capture, Direction};
|
||||
use dns_test::zone_file::Root;
|
||||
use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN};
|
||||
use dns_test::{Network, Resolver, Result, FQDN};
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn edns_support() -> Result<()> {
|
||||
let network = &Network::new()?;
|
||||
let ns = NameServer::new(&dns_test::peer(), FQDN::ROOT, network)?.start()?;
|
||||
let resolver = Resolver::start(
|
||||
&dns_test::subject(),
|
||||
&[Root::new(ns.fqdn().clone(), ns.ipv4_addr())],
|
||||
&TrustAnchor::empty(),
|
||||
network,
|
||||
)?;
|
||||
let resolver = Resolver::new(network, Root::new(ns.fqdn().clone(), ns.ipv4_addr()))
|
||||
.start(&dns_test::subject())?;
|
||||
|
||||
let mut tshark = resolver.eavesdrop()?;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use dns_test::client::{Client, DigSettings};
|
|||
use dns_test::name_server::NameServer;
|
||||
use dns_test::record::{Record, RecordType};
|
||||
use dns_test::zone_file::Root;
|
||||
use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN};
|
||||
use dns_test::{Network, Resolver, Result, FQDN};
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
|
@ -64,10 +64,13 @@ fn bad_signature_in_leaf_nameserver() -> Result<()> {
|
|||
|
||||
let root_ns = root_ns.start()?;
|
||||
|
||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
||||
|
||||
let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
||||
let resolver = Resolver::start(&dns_test::subject(), roots, &trust_anchor, &network)?;
|
||||
let resolver = Resolver::new(
|
||||
&network,
|
||||
Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr()),
|
||||
)
|
||||
.trust_anchor_key(root_ksk)
|
||||
.trust_anchor_key(root_zsk)
|
||||
.start(&dns_test::subject())?;
|
||||
let resolver_addr = resolver.ipv4_addr();
|
||||
|
||||
let client = Client::new(&network)?;
|
||||
|
|
|
@ -24,10 +24,10 @@ fn can_validate_without_delegation() -> Result<()> {
|
|||
|
||||
eprintln!("root.zone:\n{}", ns.zone_file());
|
||||
|
||||
let roots = &[Root::new(ns.fqdn().clone(), ns.ipv4_addr())];
|
||||
|
||||
let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
||||
let resolver = Resolver::start(&dns_test::subject(), roots, &trust_anchor, &network)?;
|
||||
let trust_anchor = &TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
||||
let resolver = Resolver::new(&network, Root::new(ns.fqdn().clone(), ns.ipv4_addr()))
|
||||
.trust_anchor(trust_anchor)
|
||||
.start(&dns_test::subject())?;
|
||||
let resolver_addr = resolver.ipv4_addr();
|
||||
|
||||
let client = Client::new(&network)?;
|
||||
|
@ -37,7 +37,7 @@ fn can_validate_without_delegation() -> Result<()> {
|
|||
assert!(output.status.is_noerror());
|
||||
assert!(output.flags.authenticated_data);
|
||||
|
||||
let output = client.delv(resolver_addr, RecordType::SOA, &FQDN::ROOT, &trust_anchor)?;
|
||||
let output = client.delv(resolver_addr, RecordType::SOA, &FQDN::ROOT, trust_anchor)?;
|
||||
assert!(output.starts_with("; fully validated"));
|
||||
|
||||
Ok(())
|
||||
|
@ -91,10 +91,13 @@ fn can_validate_with_delegation() -> Result<()> {
|
|||
|
||||
eprintln!("root.zone:\n{}", root_ns.zone_file());
|
||||
|
||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
||||
|
||||
let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
||||
let resolver = Resolver::start(&dns_test::subject(), roots, &trust_anchor, &network)?;
|
||||
let trust_anchor = &TrustAnchor::from_iter([root_ksk, root_zsk]);
|
||||
let resolver = Resolver::new(
|
||||
&network,
|
||||
Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr()),
|
||||
)
|
||||
.trust_anchor(trust_anchor)
|
||||
.start(&dns_test::subject())?;
|
||||
let resolver_addr = resolver.ipv4_addr();
|
||||
|
||||
let client = Client::new(&network)?;
|
||||
|
@ -111,7 +114,7 @@ fn can_validate_with_delegation() -> Result<()> {
|
|||
assert_eq!(needle_fqdn, a.fqdn);
|
||||
assert_eq!(expected_ipv4_addr, a.ipv4_addr);
|
||||
|
||||
let output = client.delv(resolver_addr, RecordType::A, &needle_fqdn, &trust_anchor)?;
|
||||
let output = client.delv(resolver_addr, RecordType::A, &needle_fqdn, trust_anchor)?;
|
||||
assert!(output.starts_with("; fully validated"));
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -44,13 +44,16 @@ fn main() -> Result<()> {
|
|||
let root_zsk = root_ns.zone_signing_key().clone();
|
||||
|
||||
let root_ns = root_ns.start()?;
|
||||
|
||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
||||
println!("DONE");
|
||||
|
||||
let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
||||
println!("building docker image...");
|
||||
let resolver = Resolver::start(&dns_test::subject(), roots, &trust_anchor, &network)?;
|
||||
let resolver = Resolver::new(
|
||||
&network,
|
||||
Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr()),
|
||||
)
|
||||
.trust_anchor(&trust_anchor)
|
||||
.start(&dns_test::subject())?;
|
||||
println!("DONE\n\n");
|
||||
|
||||
let resolver_addr = resolver.ipv4_addr();
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::net::Ipv4Addr;
|
|||
|
||||
use crate::container::{Child, Container, Network};
|
||||
use crate::implementation::{Config, Role};
|
||||
use crate::record::DNSKEY;
|
||||
use crate::trust_anchor::TrustAnchor;
|
||||
use crate::tshark::Tshark;
|
||||
use crate::zone_file::Root;
|
||||
|
@ -15,67 +16,13 @@ pub struct Resolver {
|
|||
}
|
||||
|
||||
impl Resolver {
|
||||
/// Starts a DNS server in the recursive resolver role
|
||||
///
|
||||
/// This server is not an authoritative name server; it does not server a zone file to clients
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This constructor panics if `roots` is an empty slice
|
||||
pub fn start(
|
||||
implementation: &Implementation,
|
||||
roots: &[Root],
|
||||
trust_anchor: &TrustAnchor,
|
||||
network: &Network,
|
||||
) -> Result<Self> {
|
||||
assert!(
|
||||
!roots.is_empty(),
|
||||
"must configure at least one local root server"
|
||||
);
|
||||
|
||||
let image = implementation.clone().into();
|
||||
let container = Container::run(&image, network)?;
|
||||
|
||||
let mut hints = String::new();
|
||||
for root in roots {
|
||||
writeln!(hints, "{root}").unwrap();
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(network: &Network, root: Root) -> ResolverSettings {
|
||||
ResolverSettings {
|
||||
network: network.clone(),
|
||||
roots: vec![root],
|
||||
trust_anchor: TrustAnchor::empty(),
|
||||
}
|
||||
|
||||
container.cp("/etc/root.hints", &hints)?;
|
||||
|
||||
let use_dnssec = !trust_anchor.is_empty();
|
||||
let config = Config::Resolver {
|
||||
use_dnssec,
|
||||
netmask: network.netmask(),
|
||||
};
|
||||
container.cp(
|
||||
implementation.conf_file_path(config.role()),
|
||||
&implementation.format_config(config),
|
||||
)?;
|
||||
|
||||
if use_dnssec {
|
||||
let path = if implementation.is_bind() {
|
||||
"/etc/bind/bind.keys"
|
||||
} else {
|
||||
"/etc/trusted-key.key"
|
||||
};
|
||||
|
||||
let contents = if implementation.is_bind() {
|
||||
trust_anchor.delv()
|
||||
} else {
|
||||
trust_anchor.to_string()
|
||||
};
|
||||
|
||||
container.cp(path, &contents)?;
|
||||
}
|
||||
|
||||
let child = container.spawn(implementation.cmd_args(config.role()))?;
|
||||
|
||||
Ok(Self {
|
||||
child,
|
||||
container,
|
||||
implementation: implementation.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eavesdrop(&self) -> Result<Tshark> {
|
||||
|
@ -112,6 +59,83 @@ kill -TERM $(cat {pidfile})"
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ResolverSettings {
|
||||
network: Network,
|
||||
roots: Vec<Root>,
|
||||
trust_anchor: TrustAnchor,
|
||||
}
|
||||
|
||||
impl ResolverSettings {
|
||||
/// Starts a DNS server in the recursive resolver role
|
||||
///
|
||||
/// This server is not an authoritative name server; it does not serve a zone file to clients
|
||||
pub fn start(&self, implementation: &Implementation) -> Result<Resolver> {
|
||||
let image = implementation.clone().into();
|
||||
let container = Container::run(&image, &self.network)?;
|
||||
|
||||
let mut hints = String::new();
|
||||
for root in &self.roots {
|
||||
writeln!(hints, "{root}").unwrap();
|
||||
}
|
||||
|
||||
container.cp("/etc/root.hints", &hints)?;
|
||||
|
||||
let use_dnssec = !self.trust_anchor.is_empty();
|
||||
let config = Config::Resolver {
|
||||
use_dnssec,
|
||||
netmask: self.network.netmask(),
|
||||
};
|
||||
container.cp(
|
||||
implementation.conf_file_path(config.role()),
|
||||
&implementation.format_config(config),
|
||||
)?;
|
||||
|
||||
if use_dnssec {
|
||||
let path = if implementation.is_bind() {
|
||||
"/etc/bind/bind.keys"
|
||||
} else {
|
||||
"/etc/trusted-key.key"
|
||||
};
|
||||
|
||||
let contents = if implementation.is_bind() {
|
||||
self.trust_anchor.delv()
|
||||
} else {
|
||||
self.trust_anchor.to_string()
|
||||
};
|
||||
|
||||
container.cp(path, &contents)?;
|
||||
}
|
||||
|
||||
let child = container.spawn(implementation.cmd_args(config.role()))?;
|
||||
|
||||
Ok(Resolver {
|
||||
child,
|
||||
container,
|
||||
implementation: implementation.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Adds a root hint
|
||||
pub fn root(&mut self, root: Root) -> &mut Self {
|
||||
self.roots.push(root);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a DNSKEY record to the trust anchor
|
||||
pub fn trust_anchor_key(&mut self, key: DNSKEY) -> &mut Self {
|
||||
self.trust_anchor.add(key.clone());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds all the keys in the `other` trust anchor to ours
|
||||
pub fn trust_anchor(&mut self, other: &TrustAnchor) -> &mut Self {
|
||||
for key in other.keys() {
|
||||
self.trust_anchor.add(key.clone());
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{name_server::NameServer, FQDN};
|
||||
|
@ -122,12 +146,8 @@ mod tests {
|
|||
fn terminate_unbound_works() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let ns = NameServer::new(&Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
|
||||
let resolver = Resolver::start(
|
||||
&Implementation::Unbound,
|
||||
&[Root::new(ns.fqdn().clone(), ns.ipv4_addr())],
|
||||
&TrustAnchor::empty(),
|
||||
&network,
|
||||
)?;
|
||||
let resolver = Resolver::new(&network, Root::new(ns.fqdn().clone(), ns.ipv4_addr()))
|
||||
.start(&Implementation::Unbound)?;
|
||||
let logs = resolver.terminate()?;
|
||||
|
||||
eprintln!("{logs}");
|
||||
|
@ -140,12 +160,8 @@ mod tests {
|
|||
fn terminate_bind_works() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let ns = NameServer::new(&Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
|
||||
let resolver = Resolver::start(
|
||||
&Implementation::Bind,
|
||||
&[Root::new(ns.fqdn().clone(), ns.ipv4_addr())],
|
||||
&TrustAnchor::empty(),
|
||||
&network,
|
||||
)?;
|
||||
let resolver = Resolver::new(&network, Root::new(ns.fqdn().clone(), ns.ipv4_addr()))
|
||||
.start(&Implementation::Bind)?;
|
||||
let logs = resolver.terminate()?;
|
||||
|
||||
eprintln!("{logs}");
|
||||
|
|
|
@ -20,6 +20,10 @@ impl TrustAnchor {
|
|||
self
|
||||
}
|
||||
|
||||
pub(crate) fn keys(&self) -> &[DNSKEY] {
|
||||
&self.keys
|
||||
}
|
||||
|
||||
/// formats the `TrustAnchor` in the format `delv` expects
|
||||
pub(super) fn delv(&self) -> String {
|
||||
let mut buf = "trust-anchors {".to_string();
|
||||
|
|
|
@ -248,7 +248,7 @@ mod tests {
|
|||
use crate::name_server::NameServer;
|
||||
use crate::record::{Record, RecordType};
|
||||
use crate::zone_file::Root;
|
||||
use crate::{Implementation, Network, Resolver, TrustAnchor, FQDN};
|
||||
use crate::{Implementation, Network, Resolver, FQDN};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -310,13 +310,11 @@ mod tests {
|
|||
root_ns.referral(FQDN::COM, com_ns.fqdn().clone(), com_ns.ipv4_addr());
|
||||
let root_ns = root_ns.start()?;
|
||||
|
||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
||||
let resolver = Resolver::start(
|
||||
&Implementation::Unbound,
|
||||
roots,
|
||||
&TrustAnchor::empty(),
|
||||
let resolver = Resolver::new(
|
||||
network,
|
||||
)?;
|
||||
Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr()),
|
||||
)
|
||||
.start(&Implementation::Unbound)?;
|
||||
let mut tshark = resolver.eavesdrop()?;
|
||||
let resolver_addr = resolver.ipv4_addr();
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user