Skip to content
This repository was archived by the owner on Jun 7, 2024. It is now read-only.

Commit d25cc92

Browse files
authored
Merge pull request #27 from ferrous-systems/ja-bind-impl
support using BIND in the Resolver role
2 parents b8605f7 + 90ee7b3 commit d25cc92

File tree

7 files changed

+125
-14
lines changed

7 files changed

+125
-14
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ jobs:
3030
- name: Run tests against unbound
3131
run: cargo test -p conformance-tests -- --include-ignored
3232

33+
- name: Run tests against BIND
34+
run: DNS_TEST_SUBJECT=bind cargo test -p conformance-tests -- --include-ignored
35+
3336
- name: Run tests against hickory
3437
run: |
3538
git clone https://github.com/hickory-dns/hickory-dns /tmp/hickory

packages/conformance-tests/src/resolver/dnssec/rfc4035/section_4/section_4_1.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ fn edns_support() -> Result<()> {
2121

2222
let client = Client::new(network)?;
2323
let settings = *DigSettings::default().authentic_data().recurse();
24-
let ans = client.dig(settings, resolver.ipv4_addr(), RecordType::SOA, &FQDN::ROOT)?;
25-
assert!(ans.status.is_servfail());
24+
let _ans = client.dig(settings, resolver.ipv4_addr(), RecordType::SOA, &FQDN::ROOT)?;
25+
26+
// implementation-specific behavior
27+
// unbound replies with SERVFAIL
28+
// BIND replies with NOERROR
29+
// assert!(_ans.status.is_servfail());
2630

2731
tshark.wait_for_capture()?;
2832

packages/dns-test/src/container.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const PACKAGE_NAME: &str = env!("CARGO_PKG_NAME");
2222

2323
#[derive(Clone)]
2424
pub enum Image {
25+
Bind,
2526
Client,
2627
Hickory(Repository<'static>),
2728
Unbound,
@@ -30,15 +31,21 @@ pub enum Image {
3031
impl Image {
3132
fn dockerfile(&self) -> &'static str {
3233
match self {
33-
Self::Unbound => include_str!("docker/unbound.Dockerfile"),
34-
Self::Hickory { .. } => include_str!("docker/hickory.Dockerfile"),
34+
Self::Bind => include_str!("docker/bind.Dockerfile"),
3535
Self::Client => include_str!("docker/client.Dockerfile"),
36+
Self::Hickory { .. } => include_str!("docker/hickory.Dockerfile"),
37+
Self::Unbound => include_str!("docker/unbound.Dockerfile"),
3638
}
3739
}
3840

3941
fn once(&self) -> &'static Once {
4042
match self {
41-
Self::Client { .. } => {
43+
Self::Bind => {
44+
static BIND_ONCE: Once = Once::new();
45+
&BIND_ONCE
46+
}
47+
48+
Self::Client => {
4249
static CLIENT_ONCE: Once = Once::new();
4350
&CLIENT_ONCE
4451
}
@@ -48,7 +55,7 @@ impl Image {
4855
&HICKORY_ONCE
4956
}
5057

51-
Self::Unbound { .. } => {
58+
Self::Unbound => {
5259
static UNBOUND_ONCE: Once = Once::new();
5360
&UNBOUND_ONCE
5461
}
@@ -59,6 +66,7 @@ impl Image {
5966
impl From<Implementation> for Image {
6067
fn from(implementation: Implementation) -> Self {
6168
match implementation {
69+
Implementation::Bind => Self::Bind,
6270
Implementation::Unbound => Self::Unbound,
6371
Implementation::Hickory(repo) => Self::Hickory(repo),
6472
}
@@ -69,6 +77,7 @@ impl fmt::Display for Image {
6977
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7078
let s = match self {
7179
Self::Client => "client",
80+
Self::Bind => "bind",
7281
Self::Hickory { .. } => "hickory",
7382
Self::Unbound => "unbound",
7483
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM debian:bookworm-slim
2+
3+
# ldns-utils = ldns-{key2ds,keygen,signzone}
4+
# rm = remove default configuration files
5+
RUN apt-get update && \
6+
apt-get install -y \
7+
bind9 \
8+
ldnsutils \
9+
tshark && \
10+
rm -f /etc/bind/*

packages/dns-test/src/lib.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,16 @@ const DEFAULT_TTL: u32 = 24 * 60 * 60; // 1 day
2828

2929
#[derive(Clone)]
3030
pub enum Implementation {
31-
Unbound,
31+
Bind,
3232
Hickory(Repository<'static>),
33+
Unbound,
34+
}
35+
36+
impl Implementation {
37+
#[must_use]
38+
pub fn is_bind(&self) -> bool {
39+
matches!(self, Self::Bind)
40+
}
3341
}
3442

3543
#[derive(Clone)]
@@ -70,6 +78,10 @@ pub fn subject() -> Implementation {
7078
return Implementation::Unbound;
7179
}
7280

81+
if subject == "bind" {
82+
return Implementation::Bind;
83+
}
84+
7385
if subject.starts_with("hickory") {
7486
if let Some(url) = subject.strip_prefix("hickory ") {
7587
Implementation::Hickory(Repository(url.to_string()))
@@ -85,5 +97,13 @@ pub fn subject() -> Implementation {
8597
}
8698

8799
pub fn peer() -> Implementation {
88-
Implementation::default()
100+
if let Ok(subject) = std::env::var("DNS_TEST_PEER") {
101+
match subject.as_str() {
102+
"unbound" => Implementation::Unbound,
103+
"bind" => Implementation::Bind,
104+
_ => panic!("`{subject}` is not supported as a test peer implementation"),
105+
}
106+
} else {
107+
Implementation::default()
108+
}
89109
}

packages/dns-test/src/resolver.rs

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::{Implementation, Result};
1010
pub struct Resolver {
1111
container: Container,
1212
child: Child,
13+
implementation: Implementation,
1314
}
1415

1516
impl Resolver {
@@ -26,8 +27,6 @@ impl Resolver {
2627
trust_anchor: &TrustAnchor,
2728
network: &Network,
2829
) -> Result<Self> {
29-
const TRUST_ANCHOR_FILE: &str = "/etc/trusted-key.key";
30-
3130
assert!(
3231
!roots.is_empty(),
3332
"must configure at least one local root server"
@@ -43,6 +42,15 @@ impl Resolver {
4342

4443
let use_dnssec = !trust_anchor.is_empty();
4544
match implementation {
45+
Implementation::Bind => {
46+
container.cp("/etc/bind/root.hints", &hints)?;
47+
48+
container.cp(
49+
"/etc/bind/named.conf",
50+
&named_conf(use_dnssec, network.netmask()),
51+
)?;
52+
}
53+
4654
Implementation::Unbound => {
4755
container.cp("/etc/unbound/root.hints", &hints)?;
4856

@@ -62,16 +70,33 @@ impl Resolver {
6270
}
6371

6472
if use_dnssec {
65-
container.cp(TRUST_ANCHOR_FILE, &trust_anchor.to_string())?;
73+
let path = if implementation.is_bind() {
74+
"/etc/bind/bind.keys"
75+
} else {
76+
"/etc/trusted-key.key"
77+
};
78+
79+
let contents = if implementation.is_bind() {
80+
trust_anchor.delv()
81+
} else {
82+
trust_anchor.to_string()
83+
};
84+
85+
container.cp(path, &contents)?;
6686
}
6787

6888
let command: &[_] = match implementation {
89+
Implementation::Bind => &["named", "-g", "-d5"],
6990
Implementation::Unbound => &["unbound", "-d"],
7091
Implementation::Hickory { .. } => &["hickory-dns", "-d"],
7192
};
7293
let child = container.spawn(command)?;
7394

74-
Ok(Self { child, container })
95+
Ok(Self {
96+
child,
97+
container,
98+
implementation: implementation.clone(),
99+
})
75100
}
76101

77102
pub fn eavesdrop(&self) -> Result<Tshark> {
@@ -88,7 +113,11 @@ impl Resolver {
88113

89114
/// gracefully terminates the name server collecting all logs
90115
pub fn terminate(self) -> Result<String> {
91-
let pidfile = "/run/unbound.pid";
116+
let pidfile = match self.implementation {
117+
Implementation::Bind => "/tmp/named.pid",
118+
Implementation::Unbound => "/run/unbound.pid",
119+
Implementation::Hickory(..) => unimplemented!(),
120+
};
92121
let kill = format!(
93122
"test -f {pidfile} || sleep 1
94123
kill -TERM $(cat {pidfile})"
@@ -108,6 +137,10 @@ kill -TERM $(cat {pidfile})"
108137
}
109138
}
110139

140+
fn named_conf(use_dnssec: bool, netmask: &str) -> String {
141+
minijinja::render!(include_str!("templates/named.resolver.conf.jinja"), use_dnssec => use_dnssec, netmask => netmask)
142+
}
143+
111144
fn unbound_conf(use_dnssec: bool, netmask: &str) -> String {
112145
minijinja::render!(include_str!("templates/unbound.conf.jinja"), use_dnssec => use_dnssec, netmask => netmask)
113146
}
@@ -123,7 +156,7 @@ mod tests {
123156
use super::*;
124157

125158
#[test]
126-
fn terminate_works() -> Result<()> {
159+
fn terminate_unbound_works() -> Result<()> {
127160
let network = Network::new()?;
128161
let ns = NameServer::new(&Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
129162
let resolver = Resolver::start(
@@ -139,4 +172,22 @@ mod tests {
139172

140173
Ok(())
141174
}
175+
176+
#[test]
177+
fn terminate_bind_works() -> Result<()> {
178+
let network = Network::new()?;
179+
let ns = NameServer::new(&Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
180+
let resolver = Resolver::start(
181+
&Implementation::Bind,
182+
&[Root::new(ns.fqdn().clone(), ns.ipv4_addr())],
183+
&TrustAnchor::empty(),
184+
&network,
185+
)?;
186+
let logs = resolver.terminate()?;
187+
188+
eprintln!("{logs}");
189+
assert!(logs.contains("starting BIND"));
190+
191+
Ok(())
192+
}
142193
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
options {
2+
directory "/var/cache/bind";
3+
pid-file "/tmp/named.pid";
4+
recursion yes;
5+
dnssec-validation {% if use_dnssec %} auto {% else %} no {% endif %};
6+
allow-transfer { none; };
7+
# significantly reduces noise in logs
8+
empty-zones-enable no;
9+
};
10+
11+
zone "." {
12+
type hint;
13+
file "/etc/bind/root.hints";
14+
};

0 commit comments

Comments
 (0)