Merge pull request #19 from japaric/ja-dns-test-peer
add `dns_test::peer` and use it to initialize `NameServer`
This commit is contained in:
commit
9f03274589
|
@ -12,10 +12,11 @@ fn can_resolve() -> Result<()> {
|
|||
let needle_fqdn = FQDN("example.nameservers.com.")?;
|
||||
|
||||
let network = Network::new()?;
|
||||
let mut root_ns = NameServer::new(FQDN::ROOT, &network)?;
|
||||
let mut com_ns = NameServer::new(FQDN::COM, &network)?;
|
||||
let mut root_ns = NameServer::new(dns_test::peer(), FQDN::ROOT, &network)?;
|
||||
let mut com_ns = NameServer::new(dns_test::peer(), FQDN::COM, &network)?;
|
||||
|
||||
let mut nameservers_ns = NameServer::new(FQDN("nameservers.com.")?, &network)?;
|
||||
let mut nameservers_ns =
|
||||
NameServer::new(dns_test::peer(), FQDN("nameservers.com.")?, &network)?;
|
||||
nameservers_ns
|
||||
.a(root_ns.fqdn().clone(), root_ns.ipv4_addr())
|
||||
.a(com_ns.fqdn().clone(), com_ns.ipv4_addr())
|
||||
|
@ -68,10 +69,11 @@ fn nxdomain() -> Result<()> {
|
|||
let needle_fqdn = FQDN("unicorn.nameservers.com.")?;
|
||||
|
||||
let network = Network::new()?;
|
||||
let mut root_ns = NameServer::new(FQDN::ROOT, &network)?;
|
||||
let mut com_ns = NameServer::new(FQDN::COM, &network)?;
|
||||
let mut root_ns = NameServer::new(dns_test::peer(), FQDN::ROOT, &network)?;
|
||||
let mut com_ns = NameServer::new(dns_test::peer(), FQDN::COM, &network)?;
|
||||
|
||||
let mut nameservers_ns = NameServer::new(FQDN("nameservers.com.")?, &network)?;
|
||||
let mut nameservers_ns =
|
||||
NameServer::new(dns_test::peer(), FQDN("nameservers.com.")?, &network)?;
|
||||
nameservers_ns
|
||||
.a(root_ns.fqdn().clone(), root_ns.ipv4_addr())
|
||||
.a(com_ns.fqdn().clone(), com_ns.ipv4_addr());
|
||||
|
|
|
@ -9,7 +9,7 @@ use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN};
|
|||
#[ignore]
|
||||
fn edns_support() -> Result<()> {
|
||||
let network = &Network::new()?;
|
||||
let ns = NameServer::new(FQDN::ROOT, network)?.start()?;
|
||||
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())],
|
||||
|
|
|
@ -11,7 +11,7 @@ use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN};
|
|||
#[test]
|
||||
fn can_validate_without_delegation() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let mut ns = NameServer::new(FQDN::ROOT, &network)?;
|
||||
let mut ns = NameServer::new(dns_test::peer(), FQDN::ROOT, &network)?;
|
||||
ns.a(ns.fqdn().clone(), ns.ipv4_addr());
|
||||
let ns = ns.sign()?;
|
||||
|
||||
|
@ -55,10 +55,11 @@ fn can_validate_with_delegation() -> Result<()> {
|
|||
let needle_fqdn = FQDN("example.nameservers.com.")?;
|
||||
|
||||
let network = Network::new()?;
|
||||
let mut root_ns = NameServer::new(FQDN::ROOT, &network)?;
|
||||
let mut com_ns = NameServer::new(FQDN::COM, &network)?;
|
||||
let mut root_ns = NameServer::new(dns_test::peer(), FQDN::ROOT, &network)?;
|
||||
let mut com_ns = NameServer::new(dns_test::peer(), FQDN::COM, &network)?;
|
||||
|
||||
let mut nameservers_ns = NameServer::new(FQDN("nameservers.com.")?, &network)?;
|
||||
let mut nameservers_ns =
|
||||
NameServer::new(dns_test::peer(), FQDN("nameservers.com.")?, &network)?;
|
||||
nameservers_ns
|
||||
.a(root_ns.fqdn().clone(), root_ns.ipv4_addr())
|
||||
.a(com_ns.fqdn().clone(), com_ns.ipv4_addr())
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use core::str::FromStr;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::container::{Container, Network};
|
||||
use crate::container::{Container, Image, Network};
|
||||
use crate::record::{Record, RecordType};
|
||||
use crate::trust_anchor::TrustAnchor;
|
||||
use crate::{Error, Implementation, Result, FQDN};
|
||||
use crate::{Error, Result, FQDN};
|
||||
|
||||
pub struct Client {
|
||||
inner: Container,
|
||||
|
@ -13,7 +13,7 @@ pub struct Client {
|
|||
impl Client {
|
||||
pub fn new(network: &Network) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: Container::run(&Implementation::Unbound, network)?,
|
||||
inner: Container::run(&Image::Client, network)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
mod network;
|
||||
|
||||
use core::str;
|
||||
use core::{fmt, str};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::process::{self, ChildStdout, ExitStatus};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::{atomic, Arc};
|
||||
use std::sync::{atomic, Arc, Once};
|
||||
use std::{env, fs};
|
||||
|
||||
use tempfile::{NamedTempFile, TempDir};
|
||||
|
||||
pub use crate::container::network::Network;
|
||||
use crate::{Error, Implementation, Result};
|
||||
use crate::{Error, Implementation, Repository, Result};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Container {
|
||||
|
@ -20,16 +20,72 @@ pub struct Container {
|
|||
|
||||
const PACKAGE_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Image {
|
||||
Client,
|
||||
Hickory(Repository<'static>),
|
||||
Unbound,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
fn dockerfile(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Unbound => include_str!("docker/unbound.Dockerfile"),
|
||||
Self::Hickory { .. } => include_str!("docker/hickory.Dockerfile"),
|
||||
Self::Client => include_str!("docker/client.Dockerfile"),
|
||||
}
|
||||
}
|
||||
|
||||
fn once(&self) -> &'static Once {
|
||||
match self {
|
||||
Self::Client { .. } => {
|
||||
static CLIENT_ONCE: Once = Once::new();
|
||||
&CLIENT_ONCE
|
||||
}
|
||||
|
||||
Self::Hickory { .. } => {
|
||||
static HICKORY_ONCE: Once = Once::new();
|
||||
&HICKORY_ONCE
|
||||
}
|
||||
|
||||
Self::Unbound { .. } => {
|
||||
static UNBOUND_ONCE: Once = Once::new();
|
||||
&UNBOUND_ONCE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Implementation> for Image {
|
||||
fn from(implementation: Implementation) -> Self {
|
||||
match implementation {
|
||||
Implementation::Unbound => Self::Unbound,
|
||||
Implementation::Hickory(repo) => Self::Hickory(repo),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Image {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = match self {
|
||||
Self::Client => "client",
|
||||
Self::Hickory { .. } => "hickory",
|
||||
Self::Unbound => "unbound",
|
||||
};
|
||||
f.write_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Container {
|
||||
/// Starts the container in a "parked" state
|
||||
pub fn run(implementation: &Implementation, network: &Network) -> Result<Self> {
|
||||
pub fn run(image: &Image, network: &Network) -> Result<Self> {
|
||||
// TODO make this configurable and support hickory & bind
|
||||
let dockerfile = implementation.dockerfile();
|
||||
let dockerfile = image.dockerfile();
|
||||
let docker_build_dir = TempDir::new()?;
|
||||
let docker_build_dir = docker_build_dir.path();
|
||||
fs::write(docker_build_dir.join("Dockerfile"), dockerfile)?;
|
||||
|
||||
let image_tag = format!("{PACKAGE_NAME}-{implementation}");
|
||||
let image_tag = format!("{PACKAGE_NAME}-{image}");
|
||||
|
||||
let mut command = Command::new("docker");
|
||||
command
|
||||
|
@ -37,13 +93,13 @@ impl Container {
|
|||
.arg(&image_tag)
|
||||
.arg(docker_build_dir);
|
||||
|
||||
let repo = if let Implementation::Hickory(repo) = implementation {
|
||||
let repo = if let Image::Hickory(repo) = image {
|
||||
Some(repo)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
implementation.once().call_once(|| {
|
||||
image.once().call_once(|| {
|
||||
if let Some(repo) = repo {
|
||||
let mut cp_r = Command::new("git");
|
||||
cp_r.args([
|
||||
|
@ -66,7 +122,7 @@ impl Container {
|
|||
let mut command = Command::new("docker");
|
||||
let pid = process::id();
|
||||
let count = container_count();
|
||||
let name = format!("{PACKAGE_NAME}-{implementation}-{pid}-{count}");
|
||||
let name = format!("{PACKAGE_NAME}-{image}-{pid}-{count}");
|
||||
command
|
||||
.args([
|
||||
"run",
|
||||
|
@ -334,7 +390,7 @@ mod tests {
|
|||
#[test]
|
||||
fn run_works() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let container = Container::run(&Implementation::Unbound, &network)?;
|
||||
let container = Container::run(&Image::Client, &network)?;
|
||||
|
||||
let output = container.output(&["true"])?;
|
||||
assert!(output.status.success());
|
||||
|
@ -345,7 +401,7 @@ mod tests {
|
|||
#[test]
|
||||
fn ipv4_addr_works() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let container = Container::run(&Implementation::Unbound, &network)?;
|
||||
let container = Container::run(&Image::Client, &network)?;
|
||||
let ipv4_addr = container.ipv4_addr();
|
||||
|
||||
let output = container.output(&["ping", "-c1", &format!("{ipv4_addr}")])?;
|
||||
|
@ -357,7 +413,7 @@ mod tests {
|
|||
#[test]
|
||||
fn cp_works() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let container = Container::run(&Implementation::Unbound, &network)?;
|
||||
let container = Container::run(&Image::Client, &network)?;
|
||||
|
||||
let path = "/tmp/somefile";
|
||||
let contents = "hello";
|
||||
|
|
|
@ -113,7 +113,7 @@ fn network_count() -> usize {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{container::Container, Implementation};
|
||||
use crate::container::{Container, Image};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -146,7 +146,7 @@ mod tests {
|
|||
let network = Network::new().expect("Failed to create network");
|
||||
let network_name = network.name().to_string();
|
||||
let container =
|
||||
Container::run(&Implementation::Unbound, &network).expect("Failed to start container");
|
||||
Container::run(&Image::Client, &network).expect("Failed to start container");
|
||||
|
||||
assert!(exists_network(&network_name));
|
||||
drop(network);
|
||||
|
|
8
packages/dns-test/src/docker/client.Dockerfile
Normal file
8
packages/dns-test/src/docker/client.Dockerfile
Normal file
|
@ -0,0 +1,8 @@
|
|||
FROM debian:bookworm-slim
|
||||
|
||||
# dnsutils = dig & delv
|
||||
# iputils-ping = ping
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
dnsutils \
|
||||
iputils-ping
|
|
@ -1,12 +1,8 @@
|
|||
FROM debian:bookworm-slim
|
||||
|
||||
# dnsutils = dig & delv
|
||||
# iputils-ping = ping
|
||||
# ldns-utils = ldns-{key2ds,keygen,signzone}
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
dnsutils \
|
||||
iputils-ping \
|
||||
ldnsutils \
|
||||
nsd \
|
||||
tshark \
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
//! A test framework for all things DNS
|
||||
|
||||
use core::fmt;
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::sync::Once;
|
||||
|
||||
use url::Url;
|
||||
|
||||
|
@ -57,45 +55,12 @@ pub fn Repository(input: impl Into<Cow<'static, str>>) -> Repository<'static> {
|
|||
Repository { inner: input }
|
||||
}
|
||||
|
||||
impl Implementation {
|
||||
fn dockerfile(&self) -> &'static str {
|
||||
match self {
|
||||
Implementation::Unbound => include_str!("docker/unbound.Dockerfile"),
|
||||
Implementation::Hickory { .. } => include_str!("docker/hickory.Dockerfile"),
|
||||
}
|
||||
}
|
||||
|
||||
fn once(&self) -> &'static Once {
|
||||
match self {
|
||||
Implementation::Unbound => {
|
||||
static UNBOUND_ONCE: Once = Once::new();
|
||||
&UNBOUND_ONCE
|
||||
}
|
||||
|
||||
Implementation::Hickory { .. } => {
|
||||
static HICKORY_ONCE: Once = Once::new();
|
||||
&HICKORY_ONCE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Implementation {
|
||||
fn default() -> Self {
|
||||
Self::Unbound
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Implementation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = match self {
|
||||
Implementation::Unbound => "unbound",
|
||||
Implementation::Hickory { .. } => "hickory",
|
||||
};
|
||||
f.write_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subject() -> Implementation {
|
||||
if let Ok(subject) = std::env::var("DNS_TEST_SUBJECT") {
|
||||
if subject == "unbound" {
|
||||
|
@ -115,3 +80,7 @@ pub fn subject() -> Implementation {
|
|||
Implementation::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peer() -> Implementation {
|
||||
Implementation::default()
|
||||
}
|
||||
|
|
|
@ -25,7 +25,12 @@ impl<'a> NameServer<'a, Stopped> {
|
|||
/// - one SOA record, with the primary name server field set to this name server's FQDN
|
||||
/// - one NS record, with this name server's FQDN set as the only available name server for
|
||||
/// the zone
|
||||
pub fn new(zone: FQDN<'a>, network: &Network) -> Result<Self> {
|
||||
pub fn new(implementation: Implementation, zone: FQDN<'a>, network: &Network) -> Result<Self> {
|
||||
assert!(
|
||||
matches!(implementation, Implementation::Unbound),
|
||||
"currently only `unbound` (`nsd`) can be used as a `NameServer`"
|
||||
);
|
||||
|
||||
let ns_count = ns_count();
|
||||
let nameserver = primary_ns(ns_count);
|
||||
|
||||
|
@ -42,8 +47,9 @@ impl<'a> NameServer<'a, Stopped> {
|
|||
nameserver: nameserver.clone(),
|
||||
});
|
||||
|
||||
let image = implementation.into();
|
||||
Ok(Self {
|
||||
container: Container::run(&Implementation::Unbound, network)?,
|
||||
container: Container::run(&image, network)?,
|
||||
zone_file,
|
||||
state: Stopped,
|
||||
})
|
||||
|
@ -301,7 +307,7 @@ mod tests {
|
|||
#[test]
|
||||
fn simplest() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let tld_ns = NameServer::new(FQDN::COM, &network)?.start()?;
|
||||
let tld_ns = NameServer::new(Implementation::Unbound, FQDN::COM, &network)?.start()?;
|
||||
let ip_addr = tld_ns.ipv4_addr();
|
||||
|
||||
let client = Client::new(&network)?;
|
||||
|
@ -322,7 +328,7 @@ mod tests {
|
|||
fn with_referral() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let expected_ip_addr = Ipv4Addr::new(172, 17, 200, 1);
|
||||
let mut root_ns = NameServer::new(FQDN::ROOT, &network)?;
|
||||
let mut root_ns = NameServer::new(Implementation::Unbound, FQDN::ROOT, &network)?;
|
||||
root_ns.referral(
|
||||
FQDN::COM,
|
||||
FQDN("primary.tld-server.com.")?,
|
||||
|
@ -351,7 +357,7 @@ mod tests {
|
|||
#[test]
|
||||
fn signed() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let ns = NameServer::new(FQDN::ROOT, &network)?.sign()?;
|
||||
let ns = NameServer::new(Implementation::Unbound, FQDN::ROOT, &network)?.sign()?;
|
||||
|
||||
eprintln!("KSK:\n{}", ns.key_signing_key());
|
||||
eprintln!("ZSK:\n{}", ns.zone_signing_key());
|
||||
|
@ -387,7 +393,7 @@ mod tests {
|
|||
#[test]
|
||||
fn terminate_works() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let ns = NameServer::new(FQDN::ROOT, &network)?.start()?;
|
||||
let ns = NameServer::new(Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
|
||||
let logs = ns.terminate()?;
|
||||
|
||||
assert!(logs.contains("nsd starting"));
|
||||
|
|
|
@ -33,7 +33,8 @@ impl Resolver {
|
|||
"must configure at least one local root server"
|
||||
);
|
||||
|
||||
let container = Container::run(&implementation, network)?;
|
||||
let image = implementation.clone().into();
|
||||
let container = Container::run(&image, network)?;
|
||||
|
||||
let mut hints = String::new();
|
||||
for root in roots {
|
||||
|
@ -124,7 +125,7 @@ mod tests {
|
|||
#[test]
|
||||
fn terminate_works() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let ns = NameServer::new(FQDN::ROOT, &network)?.start()?;
|
||||
let ns = NameServer::new(Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
|
||||
let resolver = Resolver::start(
|
||||
Implementation::Unbound,
|
||||
&[Root::new(ns.fqdn().clone(), ns.ipv4_addr())],
|
||||
|
|
|
@ -255,7 +255,7 @@ mod tests {
|
|||
#[test]
|
||||
fn nameserver() -> Result<()> {
|
||||
let network = &Network::new()?;
|
||||
let ns = NameServer::new(FQDN::ROOT, network)?.start()?;
|
||||
let ns = NameServer::new(Implementation::Unbound, FQDN::ROOT, network)?.start()?;
|
||||
let mut tshark = ns.eavesdrop()?;
|
||||
|
||||
let client = Client::new(network)?;
|
||||
|
@ -291,10 +291,11 @@ mod tests {
|
|||
#[test]
|
||||
fn resolver() -> Result<()> {
|
||||
let network = &Network::new()?;
|
||||
let mut root_ns = NameServer::new(FQDN::ROOT, network)?;
|
||||
let mut com_ns = NameServer::new(FQDN::COM, network)?;
|
||||
let mut root_ns = NameServer::new(Implementation::Unbound, FQDN::ROOT, network)?;
|
||||
let mut com_ns = NameServer::new(Implementation::Unbound, FQDN::COM, network)?;
|
||||
|
||||
let mut nameservers_ns = NameServer::new(FQDN("nameservers.com.")?, network)?;
|
||||
let mut nameservers_ns =
|
||||
NameServer::new(Implementation::Unbound, FQDN("nameservers.com.")?, network)?;
|
||||
nameservers_ns
|
||||
.a(root_ns.fqdn().clone(), root_ns.ipv4_addr())
|
||||
.a(com_ns.fqdn().clone(), com_ns.ipv4_addr());
|
||||
|
|
Loading…
Reference in New Issue
Block a user