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::name_server::NameServer;
|
||||||
use dns_test::record::{Record, RecordType};
|
use dns_test::record::{Record, RecordType};
|
||||||
use dns_test::zone_file::Root;
|
use dns_test::zone_file::Root;
|
||||||
use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN};
|
use dns_test::{Network, Resolver, Result, FQDN};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_resolve() -> Result<()> {
|
fn can_resolve() -> Result<()> {
|
||||||
@ -39,8 +39,11 @@ fn can_resolve() -> Result<()> {
|
|||||||
|
|
||||||
eprintln!("root.zone:\n{}", root_ns.zone_file());
|
eprintln!("root.zone:\n{}", root_ns.zone_file());
|
||||||
|
|
||||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
let resolver = Resolver::new(
|
||||||
let resolver = Resolver::start(&dns_test::subject(), roots, &TrustAnchor::empty(), &network)?;
|
&network,
|
||||||
|
Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr()),
|
||||||
|
)
|
||||||
|
.start(&dns_test::subject())?;
|
||||||
let resolver_ip_addr = resolver.ipv4_addr();
|
let resolver_ip_addr = resolver.ipv4_addr();
|
||||||
|
|
||||||
let client = Client::new(&network)?;
|
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());
|
root_ns.referral(FQDN::COM, com_ns.fqdn().clone(), com_ns.ipv4_addr());
|
||||||
let root_ns = root_ns.start()?;
|
let root_ns = root_ns.start()?;
|
||||||
|
|
||||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
let resolver = Resolver::new(
|
||||||
let resolver = Resolver::start(&dns_test::subject(), roots, &TrustAnchor::empty(), &network)?;
|
&network,
|
||||||
|
Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr()),
|
||||||
|
)
|
||||||
|
.start(&dns_test::subject())?;
|
||||||
let resolver_ip_addr = resolver.ipv4_addr();
|
let resolver_ip_addr = resolver.ipv4_addr();
|
||||||
|
|
||||||
let client = Client::new(&network)?;
|
let client = Client::new(&network)?;
|
||||||
|
@ -3,19 +3,15 @@ use dns_test::name_server::NameServer;
|
|||||||
use dns_test::record::RecordType;
|
use dns_test::record::RecordType;
|
||||||
use dns_test::tshark::{Capture, Direction};
|
use dns_test::tshark::{Capture, Direction};
|
||||||
use dns_test::zone_file::Root;
|
use dns_test::zone_file::Root;
|
||||||
use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN};
|
use dns_test::{Network, Resolver, Result, FQDN};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn edns_support() -> Result<()> {
|
fn edns_support() -> Result<()> {
|
||||||
let network = &Network::new()?;
|
let network = &Network::new()?;
|
||||||
let ns = NameServer::new(&dns_test::peer(), FQDN::ROOT, network)?.start()?;
|
let ns = NameServer::new(&dns_test::peer(), FQDN::ROOT, network)?.start()?;
|
||||||
let resolver = Resolver::start(
|
let resolver = Resolver::new(network, Root::new(ns.fqdn().clone(), ns.ipv4_addr()))
|
||||||
&dns_test::subject(),
|
.start(&dns_test::subject())?;
|
||||||
&[Root::new(ns.fqdn().clone(), ns.ipv4_addr())],
|
|
||||||
&TrustAnchor::empty(),
|
|
||||||
network,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut tshark = resolver.eavesdrop()?;
|
let mut tshark = resolver.eavesdrop()?;
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use dns_test::client::{Client, DigSettings};
|
|||||||
use dns_test::name_server::NameServer;
|
use dns_test::name_server::NameServer;
|
||||||
use dns_test::record::{Record, RecordType};
|
use dns_test::record::{Record, RecordType};
|
||||||
use dns_test::zone_file::Root;
|
use dns_test::zone_file::Root;
|
||||||
use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN};
|
use dns_test::{Network, Resolver, Result, FQDN};
|
||||||
|
|
||||||
#[ignore]
|
#[ignore]
|
||||||
#[test]
|
#[test]
|
||||||
@ -64,10 +64,13 @@ fn bad_signature_in_leaf_nameserver() -> Result<()> {
|
|||||||
|
|
||||||
let root_ns = root_ns.start()?;
|
let root_ns = root_ns.start()?;
|
||||||
|
|
||||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
let resolver = Resolver::new(
|
||||||
|
&network,
|
||||||
let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr()),
|
||||||
let resolver = Resolver::start(&dns_test::subject(), roots, &trust_anchor, &network)?;
|
)
|
||||||
|
.trust_anchor_key(root_ksk)
|
||||||
|
.trust_anchor_key(root_zsk)
|
||||||
|
.start(&dns_test::subject())?;
|
||||||
let resolver_addr = resolver.ipv4_addr();
|
let resolver_addr = resolver.ipv4_addr();
|
||||||
|
|
||||||
let client = Client::new(&network)?;
|
let client = Client::new(&network)?;
|
||||||
|
@ -24,10 +24,10 @@ fn can_validate_without_delegation() -> Result<()> {
|
|||||||
|
|
||||||
eprintln!("root.zone:\n{}", ns.zone_file());
|
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::new(&network, Root::new(ns.fqdn().clone(), ns.ipv4_addr()))
|
||||||
let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
.trust_anchor(trust_anchor)
|
||||||
let resolver = Resolver::start(&dns_test::subject(), roots, &trust_anchor, &network)?;
|
.start(&dns_test::subject())?;
|
||||||
let resolver_addr = resolver.ipv4_addr();
|
let resolver_addr = resolver.ipv4_addr();
|
||||||
|
|
||||||
let client = Client::new(&network)?;
|
let client = Client::new(&network)?;
|
||||||
@ -37,7 +37,7 @@ fn can_validate_without_delegation() -> Result<()> {
|
|||||||
assert!(output.status.is_noerror());
|
assert!(output.status.is_noerror());
|
||||||
assert!(output.flags.authenticated_data);
|
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"));
|
assert!(output.starts_with("; fully validated"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -91,10 +91,13 @@ fn can_validate_with_delegation() -> Result<()> {
|
|||||||
|
|
||||||
eprintln!("root.zone:\n{}", root_ns.zone_file());
|
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, root_zsk]);
|
||||||
|
let resolver = Resolver::new(
|
||||||
let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
&network,
|
||||||
let resolver = Resolver::start(&dns_test::subject(), roots, &trust_anchor, &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 resolver_addr = resolver.ipv4_addr();
|
||||||
|
|
||||||
let client = Client::new(&network)?;
|
let client = Client::new(&network)?;
|
||||||
@ -111,7 +114,7 @@ fn can_validate_with_delegation() -> Result<()> {
|
|||||||
assert_eq!(needle_fqdn, a.fqdn);
|
assert_eq!(needle_fqdn, a.fqdn);
|
||||||
assert_eq!(expected_ipv4_addr, a.ipv4_addr);
|
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"));
|
assert!(output.starts_with("; fully validated"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -44,13 +44,16 @@ fn main() -> Result<()> {
|
|||||||
let root_zsk = root_ns.zone_signing_key().clone();
|
let root_zsk = root_ns.zone_signing_key().clone();
|
||||||
|
|
||||||
let root_ns = root_ns.start()?;
|
let root_ns = root_ns.start()?;
|
||||||
|
|
||||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
|
||||||
println!("DONE");
|
println!("DONE");
|
||||||
|
|
||||||
let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
||||||
println!("building docker image...");
|
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");
|
println!("DONE\n\n");
|
||||||
|
|
||||||
let resolver_addr = resolver.ipv4_addr();
|
let resolver_addr = resolver.ipv4_addr();
|
||||||
|
@ -3,6 +3,7 @@ use std::net::Ipv4Addr;
|
|||||||
|
|
||||||
use crate::container::{Child, Container, Network};
|
use crate::container::{Child, Container, Network};
|
||||||
use crate::implementation::{Config, Role};
|
use crate::implementation::{Config, Role};
|
||||||
|
use crate::record::DNSKEY;
|
||||||
use crate::trust_anchor::TrustAnchor;
|
use crate::trust_anchor::TrustAnchor;
|
||||||
use crate::tshark::Tshark;
|
use crate::tshark::Tshark;
|
||||||
use crate::zone_file::Root;
|
use crate::zone_file::Root;
|
||||||
@ -15,67 +16,13 @@ pub struct Resolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Resolver {
|
impl Resolver {
|
||||||
/// Starts a DNS server in the recursive resolver role
|
#[allow(clippy::new_ret_no_self)]
|
||||||
///
|
pub fn new(network: &Network, root: Root) -> ResolverSettings {
|
||||||
/// This server is not an authoritative name server; it does not server a zone file to clients
|
ResolverSettings {
|
||||||
///
|
network: network.clone(),
|
||||||
/// # Panics
|
roots: vec![root],
|
||||||
///
|
trust_anchor: TrustAnchor::empty(),
|
||||||
/// 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{name_server::NameServer, FQDN};
|
use crate::{name_server::NameServer, FQDN};
|
||||||
@ -122,12 +146,8 @@ mod tests {
|
|||||||
fn terminate_unbound_works() -> Result<()> {
|
fn terminate_unbound_works() -> Result<()> {
|
||||||
let network = Network::new()?;
|
let network = Network::new()?;
|
||||||
let ns = NameServer::new(&Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
|
let ns = NameServer::new(&Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
|
||||||
let resolver = Resolver::start(
|
let resolver = Resolver::new(&network, Root::new(ns.fqdn().clone(), ns.ipv4_addr()))
|
||||||
&Implementation::Unbound,
|
.start(&Implementation::Unbound)?;
|
||||||
&[Root::new(ns.fqdn().clone(), ns.ipv4_addr())],
|
|
||||||
&TrustAnchor::empty(),
|
|
||||||
&network,
|
|
||||||
)?;
|
|
||||||
let logs = resolver.terminate()?;
|
let logs = resolver.terminate()?;
|
||||||
|
|
||||||
eprintln!("{logs}");
|
eprintln!("{logs}");
|
||||||
@ -140,12 +160,8 @@ mod tests {
|
|||||||
fn terminate_bind_works() -> Result<()> {
|
fn terminate_bind_works() -> Result<()> {
|
||||||
let network = Network::new()?;
|
let network = Network::new()?;
|
||||||
let ns = NameServer::new(&Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
|
let ns = NameServer::new(&Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
|
||||||
let resolver = Resolver::start(
|
let resolver = Resolver::new(&network, Root::new(ns.fqdn().clone(), ns.ipv4_addr()))
|
||||||
&Implementation::Bind,
|
.start(&Implementation::Bind)?;
|
||||||
&[Root::new(ns.fqdn().clone(), ns.ipv4_addr())],
|
|
||||||
&TrustAnchor::empty(),
|
|
||||||
&network,
|
|
||||||
)?;
|
|
||||||
let logs = resolver.terminate()?;
|
let logs = resolver.terminate()?;
|
||||||
|
|
||||||
eprintln!("{logs}");
|
eprintln!("{logs}");
|
||||||
|
@ -20,6 +20,10 @@ impl TrustAnchor {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn keys(&self) -> &[DNSKEY] {
|
||||||
|
&self.keys
|
||||||
|
}
|
||||||
|
|
||||||
/// formats the `TrustAnchor` in the format `delv` expects
|
/// formats the `TrustAnchor` in the format `delv` expects
|
||||||
pub(super) fn delv(&self) -> String {
|
pub(super) fn delv(&self) -> String {
|
||||||
let mut buf = "trust-anchors {".to_string();
|
let mut buf = "trust-anchors {".to_string();
|
||||||
|
@ -248,7 +248,7 @@ mod tests {
|
|||||||
use crate::name_server::NameServer;
|
use crate::name_server::NameServer;
|
||||||
use crate::record::{Record, RecordType};
|
use crate::record::{Record, RecordType};
|
||||||
use crate::zone_file::Root;
|
use crate::zone_file::Root;
|
||||||
use crate::{Implementation, Network, Resolver, TrustAnchor, FQDN};
|
use crate::{Implementation, Network, Resolver, FQDN};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -310,13 +310,11 @@ mod tests {
|
|||||||
root_ns.referral(FQDN::COM, com_ns.fqdn().clone(), com_ns.ipv4_addr());
|
root_ns.referral(FQDN::COM, com_ns.fqdn().clone(), com_ns.ipv4_addr());
|
||||||
let root_ns = root_ns.start()?;
|
let root_ns = root_ns.start()?;
|
||||||
|
|
||||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
let resolver = Resolver::new(
|
||||||
let resolver = Resolver::start(
|
|
||||||
&Implementation::Unbound,
|
|
||||||
roots,
|
|
||||||
&TrustAnchor::empty(),
|
|
||||||
network,
|
network,
|
||||||
)?;
|
Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr()),
|
||||||
|
)
|
||||||
|
.start(&Implementation::Unbound)?;
|
||||||
let mut tshark = resolver.eavesdrop()?;
|
let mut tshark = resolver.eavesdrop()?;
|
||||||
let resolver_addr = resolver.ipv4_addr();
|
let resolver_addr = resolver.ipv4_addr();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user