diff --git a/packages/conformance-tests/src/resolver/dnssec/fixtures.rs b/packages/conformance-tests/src/resolver/dnssec/fixtures.rs index eb326de9..5167773b 100644 --- a/packages/conformance-tests/src/resolver/dnssec/fixtures.rs +++ b/packages/conformance-tests/src/resolver/dnssec/fixtures.rs @@ -2,9 +2,9 @@ use std::net::Ipv4Addr; use base64::prelude::*; use dns_test::{ - name_server::{Graph, NameServer, Sign}, + name_server::{Graph, NameServer, Running, Sign}, record::Record, - Network, Resolver, Result, FQDN, + Network, Resolver, Result, TrustAnchor, FQDN, }; pub fn bad_signature_in_leaf_nameserver( @@ -48,3 +48,28 @@ pub fn bad_signature_in_leaf_nameserver( Ok((resolver, graph)) } + +pub fn minimally_secure( + leaf_fqdn: FQDN, + leaf_ipv4_addr: Ipv4Addr, +) -> Result<(Resolver, Vec>, TrustAnchor)> { + assert_eq!(Some(FQDN::NAMESERVERS), leaf_fqdn.parent()); + + let network = Network::new()?; + + let mut leaf_ns = NameServer::new(&dns_test::PEER, FQDN::NAMESERVERS, &network)?; + leaf_ns.add(Record::a(leaf_fqdn.clone(), leaf_ipv4_addr)); + + let Graph { + nameservers, + root, + trust_anchor, + } = Graph::build(leaf_ns, Sign::Yes)?; + + let trust_anchor = trust_anchor.unwrap(); + let resolver = Resolver::new(&network, root) + .trust_anchor(&trust_anchor) + .start(&dns_test::SUBJECT)?; + + Ok((resolver, nameservers, trust_anchor)) +} diff --git a/packages/conformance-tests/src/resolver/dnssec/rfc4035/section_4.rs b/packages/conformance-tests/src/resolver/dnssec/rfc4035/section_4.rs index 5779d78f..52bb840b 100644 --- a/packages/conformance-tests/src/resolver/dnssec/rfc4035/section_4.rs +++ b/packages/conformance-tests/src/resolver/dnssec/rfc4035/section_4.rs @@ -1 +1,2 @@ mod section_4_1; +mod section_4_6; diff --git a/packages/conformance-tests/src/resolver/dnssec/rfc4035/section_4/section_4_6.rs b/packages/conformance-tests/src/resolver/dnssec/rfc4035/section_4/section_4_6.rs new file mode 100644 index 00000000..392a4623 --- /dev/null +++ b/packages/conformance-tests/src/resolver/dnssec/rfc4035/section_4/section_4_6.rs @@ -0,0 +1,70 @@ +use std::net::Ipv4Addr; + +use dns_test::{ + client::{Client, DigSettings}, + record::RecordType, + tshark::{Capture, Direction}, + Result, FQDN, +}; + +use crate::resolver::dnssec::fixtures; + +#[test] +fn clears_ad_bit_in_outgoing_queries() -> Result<()> { + let leaf_ipv4_addr = Ipv4Addr::new(1, 2, 3, 4); + let leaf_fqdn = FQDN("example.nameservers.com.")?; + + let (resolver, nameservers, _trust_anchor) = + fixtures::minimally_secure(leaf_fqdn.clone(), leaf_ipv4_addr)?; + + let mut tshark = resolver.eavesdrop()?; + + let resolver_addr = resolver.ipv4_addr(); + + let client = Client::new(resolver.network())?; + let settings = *DigSettings::default().recurse().authentic_data(); + let _output = client.dig(settings, resolver_addr, RecordType::A, &leaf_fqdn)?; + + tshark.wait_for_capture()?; + let captures = tshark.terminate()?; + + let client_addr = client.ipv4_addr(); + let mut ns_checks_count = 0; + let mut client_checks_count = 0; + let ns_addrs = nameservers + .iter() + .map(|ns| ns.ipv4_addr()) + .collect::>(); + for Capture { message, direction } in captures { + match direction { + Direction::Incoming { source } => { + if source == client_addr { + // sanity check + assert!(message.is_ad_flag_set()); + + client_checks_count += 1; + } + } + + Direction::Outgoing { destination } => { + if destination == client_addr { + // skip response to client + continue; + } + + // sanity check + assert!(ns_addrs.contains(&destination)); + + assert!(!message.is_ad_flag_set()); + + ns_checks_count += 1; + } + } + } + + // sanity checks + assert_eq!(1, client_checks_count); + assert_ne!(0, dbg!(ns_checks_count)); + + Ok(()) +} diff --git a/packages/conformance-tests/src/resolver/dnssec/scenarios/secure.rs b/packages/conformance-tests/src/resolver/dnssec/scenarios/secure.rs index bf076c0b..4f2d5579 100644 --- a/packages/conformance-tests/src/resolver/dnssec/scenarios/secure.rs +++ b/packages/conformance-tests/src/resolver/dnssec/scenarios/secure.rs @@ -1,11 +1,13 @@ use std::net::Ipv4Addr; use dns_test::client::{Client, DigSettings}; -use dns_test::name_server::{Graph, NameServer, Sign}; +use dns_test::name_server::NameServer; use dns_test::record::{Record, RecordType}; use dns_test::zone_file::Root; use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN}; +use crate::resolver::dnssec::fixtures; + // no DS records are involved; this is a single-link chain of trust #[ignore] #[test] @@ -49,24 +51,12 @@ fn can_validate_with_delegation() -> Result<()> { let expected_ipv4_addr = Ipv4Addr::new(1, 2, 3, 4); let needle_fqdn = FQDN("example.nameservers.com.")?; - let network = Network::new()?; + let (resolver, _nameservers, trust_anchor) = + fixtures::minimally_secure(needle_fqdn.clone(), expected_ipv4_addr)?; - let mut leaf_ns = NameServer::new(&dns_test::PEER, FQDN::NAMESERVERS, &network)?; - leaf_ns.add(Record::a(needle_fqdn.clone(), expected_ipv4_addr)); - - let Graph { - nameservers: _nameservers, - root, - trust_anchor, - } = Graph::build(leaf_ns, Sign::Yes)?; - - let trust_anchor = &trust_anchor.unwrap(); - let resolver = Resolver::new(&network, root) - .trust_anchor(trust_anchor) - .start(&dns_test::SUBJECT)?; let resolver_addr = resolver.ipv4_addr(); - let client = Client::new(&network)?; + let client = Client::new(resolver.network())?; let settings = *DigSettings::default().recurse().authentic_data(); let output = client.dig(settings, resolver_addr, RecordType::A, &needle_fqdn)?; @@ -80,7 +70,7 @@ fn can_validate_with_delegation() -> Result<()> { assert_eq!(needle_fqdn, a.fqdn); 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")); Ok(()) diff --git a/packages/dns-test/src/tshark.rs b/packages/dns-test/src/tshark.rs index c78e1903..1307f4f7 100644 --- a/packages/dns-test/src/tshark.rs +++ b/packages/dns-test/src/tshark.rs @@ -183,6 +183,20 @@ impl Message { &self.inner } + pub fn is_ad_flag_set(&self) -> bool { + let Some(authenticated) = self.inner["dns.flags_tree"] + .as_object() + .unwrap() + .get("dns.flags.authenticated") + else { + return false; + }; + + let authenticated = authenticated.as_str().unwrap(); + assert_eq!("1", authenticated); + true + } + fn opt_record(&self) -> Option<&serde_json::Value> { for (key, value) in self.inner.get("Additional records")?.as_object()? { if key.ends_with(": type OPT") {