use std::time::{Duration, Instant};
use futures::{future, prelude::*};
use tdns_cli::{
record::RecordSet,
update::{monitor_update, perform_update, Expectation, Monitor, Operation, Update},
DnsOpen,
};
use tokio::{runtime::current_thread::Runtime, timer::Delay};
use trust_dns::rr;
mod mock;
use mock::{parse_rdata, ZoneEntries};
const TIMEOUT: Duration = Duration::from_millis(10);
fn monitor_settings(expected: &str) -> Monitor {
let rset = RecordSet::new(
"foo.example.org".parse().unwrap(),
expected.parse().unwrap(),
);
Monitor {
zone: "example.org".parse().unwrap(),
entry: "foo.example.org".parse().unwrap(),
expectation: if rset.is_empty() {
Expectation::Empty(rset.record_type())
} else {
Expectation::Is(rset)
},
exclude: Default::default(),
interval: TIMEOUT / 100,
timeout: TIMEOUT,
verbose: true,
}
}
fn update_settings(operation: Operation) -> Update {
Update {
zone: "example.org".parse().unwrap(),
server: None,
operation,
tsig_key: None,
ttl: 300,
}
}
fn mock_dns(master_data: ZoneEntries) -> (mock::Open, mock::Handle<mock::Server>) {
let rec_data: &[_] = &[
(
"example.org",
"SOA",
"sns.dns.icann.org. noc.dns.icann.org. 2019090512 7200 3600 1209600 3600",
),
("example.org", "NS", "a.iana-servers.net."),
("example.org", "NS", "b.iana-servers.net."),
("a.iana-servers.net", "A", "199.43.135.53"),
("b.iana-servers.net", "A", "199.43.133.53"),
("sns.dns.icann.org", "A", "192.0.32.162"),
];
let rec_addr = "127.0.0.1:53".parse().unwrap();
let master_addr = "192.0.32.162:53".parse().unwrap();
let mut mock = mock::Open::default();
mock.add_server(rec_addr, rec_data).unwrap();
let master = mock.add_server(master_addr, master_data).unwrap();
(mock, master)
}
fn mock_dns_fixed(
master_data: ZoneEntries,
auth1_data: ZoneEntries,
auth2_data: ZoneEntries,
) -> mock::Open {
let (mut dns, _) = mock_dns(master_data);
let auth1_addr = "199.43.135.53:53".parse().unwrap();
let auth2_addr = "199.43.133.53:53".parse().unwrap();
dns.add_server(auth1_addr, auth1_data).unwrap();
dns.add_server(auth2_addr, auth2_data).unwrap();
dns
}
fn mock_dns_shared(master_data: ZoneEntries) -> (mock::Open, mock::Handle<mock::Zone>) {
let (mut dns, master) = mock_dns(master_data);
let auth1_addr = "199.43.135.53:53".parse().unwrap();
let auth2_addr = "199.43.133.53:53".parse().unwrap();
let master = master.lock().unwrap();
dns.add_shared(auth1_addr, master.zone());
dns.add_shared(auth2_addr, master.zone());
(dns, master.zone())
}
fn mock_dns_independent(master_data: ZoneEntries) -> (mock::Open, mock::Handle<mock::Zone>) {
let (mut dns, _) = mock_dns(master_data);
let auth1_addr = "199.43.135.53:53".parse().unwrap();
let auth2_addr = "199.43.133.53:53".parse().unwrap();
let slave = dns.add_server(auth1_addr, master_data).unwrap();
let slave = slave.lock().unwrap();
dns.add_shared(auth2_addr, slave.zone());
(dns, slave.zone())
}
#[test]
fn test_monitor_match() {
let mut runtime = Runtime::new().unwrap();
let (mut dns, _) = mock_dns_shared(&[("foo.example.org", "A", "192.168.1.1")]);
let resolver = dns.open(runtime.handle(), "127.0.0.1:53".parse().unwrap());
let monitor = monitor_update(
runtime.handle(),
dns,
resolver,
monitor_settings("A:192.168.1.1"),
);
runtime.block_on(monitor).unwrap();
}
#[test]
fn test_monitor_mismatch() {
let mut runtime = Runtime::new().unwrap();
let mut dns = mock_dns_fixed(
&[("foo.example.org", "A", "192.168.1.1")],
&[("foo.example.org", "A", "192.168.1.1")],
&[("foo.example.org", "A", "192.168.1.2")],
);
let resolver = dns.open(runtime.handle(), "127.0.0.1:53".parse().unwrap());
let monitor = monitor_update(
runtime.handle(),
dns,
resolver,
monitor_settings("A:192.168.1.1"),
);
let result = runtime.block_on(monitor);
assert!(result.is_err()); }
#[test]
fn test_create_immediate() {
let mut runtime = Runtime::new().unwrap();
let (mut dns, _) = mock_dns_shared(&[("foo.example.org", "A", "192.168.1.1")]);
let resolver = dns.open(runtime.handle(), "127.0.0.1:53".parse().unwrap());
let update = perform_update(
runtime.handle(),
dns.clone(),
resolver.clone(),
update_settings(Operation::Create(RecordSet::new(
"foo.example.org".parse().unwrap(),
"A:192.168.1.2".parse().unwrap(),
))),
)
.unwrap();
let monitor = monitor_update(
runtime.handle(),
dns,
resolver,
monitor_settings("A:192.168.1.2"),
);
runtime.block_on(update.and_then(|_| monitor)).unwrap();
}
#[test]
fn test_create_delayed() {
let mut runtime = Runtime::new().unwrap();
let (mut dns, zone) = mock_dns_independent(&[("foo.example.org", "A", "192.168.1.1")]);
let resolver = dns.open(runtime.handle(), "127.0.0.1:53".parse().unwrap());
let update = perform_update(
runtime.handle(),
dns,
resolver,
update_settings(Operation::create(
"foo.example.org".parse().unwrap(),
"A:192.168.1.2".parse().unwrap(),
)),
)
.unwrap();
let updated = rr::Record::from_rdata(
"foo.example.org".parse().unwrap(),
0,
parse_rdata("A", "192.168.1.2").unwrap(),
);
let update_auth = Delay::new(Instant::now() + TIMEOUT / 2)
.map(|_| {
let mut zone = zone.lock().unwrap();
zone.update(&updated);
})
.map_err(Into::into);
runtime
.block_on(future::join_all(vec![
Box::new(update) as Box<dyn Future<Item = (), Error = failure::Error>>,
Box::new(update_auth),
]))
.unwrap();
}
#[test]
fn test_delete() {
let mut runtime = Runtime::new().unwrap();
let (mut dns, _) = mock_dns_shared(&[("foo.example.org", "A", "192.168.1.1")]);
let resolver = dns.open(runtime.handle(), "127.0.0.1:53".parse().unwrap());
let update = perform_update(
runtime.handle(),
dns.clone(),
resolver.clone(),
update_settings(Operation::delete(
"foo.example.org".parse().unwrap(),
"A:192.168.1.1".parse().unwrap(),
)),
)
.unwrap();
let monitor = monitor_update(runtime.handle(), dns, resolver, monitor_settings("A"));
runtime.block_on(update.and_then(|_| monitor)).unwrap();
}