refactor: use builder pattern in Resolver ctor
the `start` constructor's parameter list was getting long and we want to add even more configuration options, like EDE, in the future. using the builder pattern lets us introduce new settings without breaking changes
This commit is contained in:
parent
63c95fd0db
commit
70245e7ff8
@ -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