add fallthrough flag

zone

test log
This commit is contained in:
Luna Yao
2026-04-18 02:45:45 +02:00
parent 9332baf6f9
commit bc86917dea
6 changed files with 119 additions and 19 deletions
+5 -6
View File
@@ -1,14 +1,14 @@
use derive_more::{Deref, DerefMut};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct AclPolicy {
pub whitelist: Option<Vec<String>>,
pub blacklist: Option<Vec<String>>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default, Deref, DerefMut)]
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize, Deref, DerefMut)]
#[serde(default)]
pub struct FunctionalityPolicy {
#[serde(flatten)]
@@ -18,7 +18,7 @@ pub struct FunctionalityPolicy {
pub disabled: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default, Deref, DerefMut)]
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize, Deref, DerefMut)]
#[serde(default)]
pub struct DnsPolicy<P = FunctionalityPolicy> {
#[serde(flatten)]
@@ -32,16 +32,15 @@ pub type ZoneExportPolicy = FunctionalityPolicy;
pub type DnsExportPolicy = DnsPolicy<ZoneExportPolicy>;
pub type DnsImportPolicy = DnsPolicy<FunctionalityPolicy>;
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct DnsPolicyConfig {
pub import: DnsImportPolicy,
pub export: Option<DnsExportPolicy>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct ZonePolicyConfig {
#[serde(default)]
pub export: Option<DnsExportPolicy>,
}
+7 -5
View File
@@ -69,20 +69,21 @@ impl ZoneConfig {
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
#[derive(Derivative, Debug, Clone, PartialEq, Deserialize, Serialize)]
#[derivative(Default)]
#[serde(default)]
pub struct ZoneConfigInner {
#[serde(default = "Uuid::new_v4")]
#[derivative(Default(value = "Uuid::new_v4()"))]
#[serde(skip_serializing)]
id: Uuid,
pub origin: LowerName,
#[serde(default)]
pub ttl: u32,
#[serde(default)]
pub records: Vec<String>,
#[serde(default)]
pub forwarders: NameServerAddrGroup,
#[serde(flatten)]
pub policy: ZonePolicyConfig,
#[derivative(Default(value = "true"))]
pub fallthrough: bool,
}
impl From<ZoneConfigInner> for ZoneData {
@@ -93,6 +94,7 @@ impl From<ZoneConfigInner> for ZoneData {
ttl: value.ttl,
records: value.records,
forwarders: value.forwarders.into(),
fallthrough: value.fallthrough,
}
}
}
+1
View File
@@ -250,6 +250,7 @@ mod tests {
ttl: 60,
records: vec!["@ IN A 10.0.0.11".to_string()],
forwarders: vec![],
fallthrough: false,
}],
fqdn: "invalid.peer.test".to_string(),
};
+14 -4
View File
@@ -114,6 +114,7 @@ pub fn zone_data_a_with_forwarders(origin: &str, record: &str, forwarders: Vec<&
.into_iter()
.map(|f| Url::from_str(f).expect("invalid forwarder"))
.collect(),
fallthrough: false,
}
}
@@ -344,8 +345,10 @@ async fn wait_peer_zone_visibility(
loop {
dns.refresh(target_peer_id).await;
let visible = dns
.snapshot()
let snapshot = dns.snapshot();
let visible = snapshot
.zones
.iter()
.any(|z| z.origin.contains(zone_origin_substr));
@@ -354,12 +357,19 @@ async fn wait_peer_zone_visibility(
return;
}
let origins = snapshot
.zones
.iter()
.map(|z| z.origin.clone())
.collect::<Vec<_>>();
assert!(
Instant::now() < deadline,
"zone visibility mismatch for '{}': expected {}, got {}",
"zone visibility mismatch for '{}': expected {}, got {}, current origins: {:?}",
zone_origin_substr,
expected_visible,
visible
visible,
origins
);
tokio::time::sleep(Duration::from_millis(200)).await;
}
+91 -4
View File
@@ -1,6 +1,6 @@
use crate::common::dns::get_default_resolver_config;
use crate::dns::utils::addr::{NameServerAddr, NameServerAddrGroup};
use crate::dns::utils::zone_handler::ArcZoneHandler;
use crate::dns::utils::zone_handler::{ArcZoneHandler, ChainedZoneHandler};
use crate::proto;
use crate::proto::utils::RepeatedMessageModel;
use hickory_net::runtime::TokioRuntimeProvider;
@@ -23,6 +23,7 @@ pub struct Zone {
origin: LowerName,
records: BTreeMap<RrKey, RecordSet>,
pub forward: Option<ForwardConfig>,
fallthrough: bool,
}
impl Zone {
@@ -35,6 +36,7 @@ impl Zone {
};
let mut zone = Self::new(".".parse().unwrap());
zone.forward = Some(forward);
zone.fallthrough = false;
zone
}
}
@@ -46,6 +48,7 @@ impl Zone {
origin: name,
records: BTreeMap::new(),
forward: None,
fallthrough: true,
}
}
@@ -64,7 +67,11 @@ impl Zone {
.map(|(k, v)| (k, Arc::new(v))),
);
Arc::new(memory) as ArcZoneHandler
if self.fallthrough {
Arc::new(ChainedZoneHandler::from(memory)) as _
} else {
Arc::new(memory) as _
}
})
}
@@ -77,7 +84,13 @@ impl Zone {
.build()
.inspect_err(|e| tracing::error!("failed to create forward zone_handler: {:?}", e))
.ok()
.map(|f| Arc::new(f) as ArcZoneHandler)
.map(|f| {
if self.fallthrough {
Arc::new(ChainedZoneHandler::from(f)) as _
} else {
Arc::new(f) as _
}
})
})
}
}
@@ -111,6 +124,7 @@ impl TryFrom<&proto::dns::ZoneData> for Zone {
origin: origin.into(),
records,
forward,
fallthrough: value.fallthrough,
})
}
}
@@ -139,6 +153,7 @@ impl From<Zone> for proto::dns::ZoneData {
ttl: 0,
records,
forwarders,
fallthrough: value.fallthrough,
}
}
}
@@ -195,7 +210,12 @@ mod tests {
}
}
fn zone_data(origin: &str, records: Vec<&str>, forwarders: Vec<&str>) -> ZoneData {
fn zone_data_with_fallthrough(
origin: &str,
records: Vec<&str>,
forwarders: Vec<&str>,
fallthrough: bool,
) -> ZoneData {
ZoneData {
id: Some(Uuid::new_v4().into()),
origin: origin.to_string(),
@@ -205,9 +225,14 @@ mod tests {
.into_iter()
.map(|f| Url::from_str(f).expect("invalid forwarder"))
.collect(),
fallthrough,
}
}
fn zone_data(origin: &str, records: Vec<&str>, forwarders: Vec<&str>) -> ZoneData {
zone_data_with_fallthrough(origin, records, forwarders, true)
}
fn build_catalog(zones: ZoneGroup) -> Catalog {
zones
.into_groups()
@@ -273,6 +298,7 @@ mod tests {
ttl: 60,
records: vec!["@ IN A 10.0.0.1".to_string()],
forwarders: vec![],
fallthrough: false,
};
let err = Zone::try_from(&data).expect_err("missing id should fail");
@@ -416,6 +442,67 @@ mod tests {
Ok(())
}
#[tokio::test]
async fn catalog_lookup_falls_back_to_later_zone_handler_with_same_origin() -> anyhow::Result<()>
{
let zones: ZoneGroup = vec![
// First matching zone exists but does not contain the queried name.
Zone::try_from(&zone_data(
"fallback.test",
vec!["first IN A 10.20.30.1"],
vec![],
))?,
// Second matching zone should be queried as fallback and answer.
Zone::try_from(&zone_data(
"fallback.test",
vec!["target IN A 10.20.30.2"],
vec![],
))?,
]
.into();
let catalog = build_catalog(zones);
let (rcode, message) =
lookup_message(&catalog, "target.fallback.test.", RecordType::A).await?;
assert_eq!(rcode, ResponseCode::NoError);
assert!(has_a_answer(
&message.expect("response should exist"),
Ipv4Addr::new(10, 20, 30, 2)
));
Ok(())
}
#[tokio::test]
async fn catalog_lookup_does_not_fall_back_when_fallthrough_disabled() -> anyhow::Result<()> {
let zones: ZoneGroup = vec![
Zone::try_from(&zone_data_with_fallthrough(
"fallback-disabled.test",
vec!["first IN A 10.20.31.1"],
vec![],
false,
))?,
Zone::try_from(&zone_data_with_fallthrough(
"fallback-disabled.test",
vec!["target IN A 10.20.31.2"],
vec![],
false,
))?,
]
.into();
let catalog = build_catalog(zones);
let (rcode, message) =
lookup_message(&catalog, "target.fallback-disabled.test.", RecordType::A).await?;
assert_ne!(rcode, ResponseCode::NoError);
if let Some(message) = message.as_ref() {
assert!(!has_a_answer(message, Ipv4Addr::new(10, 20, 31, 2)));
}
Ok(())
}
#[tokio::test]
async fn catalog_forward_only_zone_queries_upstream() -> anyhow::Result<()> {
let (upstream_addr, upstream_handle) = start_upstream_server().await?;
+1
View File
@@ -10,6 +10,7 @@ message ZoneData {
uint32 ttl = 3;
repeated string records = 4;
repeated common.Url forwarders = 5;
bool fallthrough = 6;
}
message GetExportConfigRequest {}