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) { 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) { 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) { 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()); // TODO: check for timeout error, specifically } #[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>, 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(); }