mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-03 01:25:37 +00:00
zone: fallthrough policy
heartbeat test try_from
This commit is contained in:
@@ -71,6 +71,7 @@ tokio-util = { version = "0.7.9", features = ["codec", "net", "io", "rt"] }
|
|||||||
async-stream = "0.3.5"
|
async-stream = "0.3.5"
|
||||||
async-trait = "0.1.74"
|
async-trait = "0.1.74"
|
||||||
|
|
||||||
|
maplit = "1.0.2"
|
||||||
dashmap = "6.0"
|
dashmap = "6.0"
|
||||||
timedmap = "=1.0.1"
|
timedmap = "=1.0.1"
|
||||||
|
|
||||||
|
|||||||
@@ -735,7 +735,7 @@ impl DnsGlobalCtxExt for GlobalCtx {
|
|||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| dns::parse(self.get_hostname()))
|
.unwrap_or_else(|| dns::parse(self.get_hostname()))
|
||||||
.into();
|
.into();
|
||||||
let fqdn = name.append_domain(&*dns.domain).unwrap_or_default().into();
|
let fqdn = name.append_domain(&dns.domain).unwrap_or_default().into();
|
||||||
let ipv4 = self.get_ipv4().map(|ip| ip.address());
|
let ipv4 = self.get_ipv4().map(|ip| ip.address());
|
||||||
let ipv6 = self.get_ipv6().map(|ip| ip.address());
|
let ipv6 = self.get_ipv6().map(|ip| ip.address());
|
||||||
let ipv6 = ipv6.map(|a| vec![a]).unwrap_or_default();
|
let ipv6 = ipv6.map(|a| vec![a]).unwrap_or_default();
|
||||||
|
|||||||
@@ -3,12 +3,40 @@ use crate::dns::config::policy::{DnsExportPolicy, ZonePolicyConfig};
|
|||||||
use crate::dns::utils::addr::NameServerAddrGroup;
|
use crate::dns::utils::addr::NameServerAddrGroup;
|
||||||
use crate::dns::zone::Zone;
|
use crate::dns::zone::Zone;
|
||||||
use crate::proto::dns::ZoneData;
|
use crate::proto::dns::ZoneData;
|
||||||
|
use derive_more::From;
|
||||||
|
use hickory_proto::op::ResponseCode;
|
||||||
use hickory_proto::rr::LowerName;
|
use hickory_proto::rr::LowerName;
|
||||||
|
use maplit::hashset;
|
||||||
use optional_struct::{Applicable, optional_struct};
|
use optional_struct::{Applicable, optional_struct};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
use url::Url;
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash, From, Deserialize, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Fallthrough {
|
||||||
|
Any,
|
||||||
|
ResponseCode(ResponseCode),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Fallthrough> for i32 {
|
||||||
|
fn from(value: Fallthrough) -> Self {
|
||||||
|
match value {
|
||||||
|
Fallthrough::ResponseCode(code) => u16::from(code).into(),
|
||||||
|
Fallthrough::Any => -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i32> for Fallthrough {
|
||||||
|
fn from(value: i32) -> Self {
|
||||||
|
match u16::try_from(value) {
|
||||||
|
Ok(value) => Self::ResponseCode(value.into()),
|
||||||
|
Err(_) => Self::Any,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[optional_struct(ZoneConfigRaw)]
|
#[optional_struct(ZoneConfigRaw)]
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Deserialize, Serialize)]
|
||||||
@@ -21,7 +49,7 @@ pub struct ZoneConfigParsed {
|
|||||||
#[optional_skip_wrap]
|
#[optional_skip_wrap]
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub policy: ZonePolicyConfig,
|
pub policy: ZonePolicyConfig,
|
||||||
pub fallthrough: bool,
|
pub fallthrough: HashSet<Fallthrough>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&ZoneConfigParsed> for ZoneData {
|
impl From<&ZoneConfigParsed> for ZoneData {
|
||||||
@@ -30,8 +58,8 @@ impl From<&ZoneConfigParsed> for ZoneData {
|
|||||||
&value.origin,
|
&value.origin,
|
||||||
value.ttl,
|
value.ttl,
|
||||||
&value.records,
|
&value.records,
|
||||||
value.forwarders.iter().map(Url::from),
|
value.forwarders.iter().map(Into::into),
|
||||||
value.fallthrough,
|
value.fallthrough.iter().copied(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,7 +71,7 @@ impl TryFrom<ZoneConfigRaw> for ZoneConfig {
|
|||||||
|
|
||||||
fn try_from(raw: ZoneConfigRaw) -> Result<Self, Self::Error> {
|
fn try_from(raw: ZoneConfigRaw) -> Result<Self, Self::Error> {
|
||||||
let default = ZoneConfigParsed {
|
let default = ZoneConfigParsed {
|
||||||
fallthrough: true,
|
fallthrough: hashset! {Fallthrough::Any},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -254,10 +254,10 @@ mod tests {
|
|||||||
use crate::peers::peer_manager::RouteAlgoType;
|
use crate::peers::peer_manager::RouteAlgoType;
|
||||||
use crate::peers::tests::{connect_peer_manager, wait_route_appear};
|
use crate::peers::tests::{connect_peer_manager, wait_route_appear};
|
||||||
use crate::proto::dns::GetExportConfigRequest;
|
use crate::proto::dns::GetExportConfigRequest;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use tokio::time::{Duration, sleep};
|
use tokio::time::{Duration, sleep};
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
async fn create_peer_manager_with_zone(
|
async fn create_peer_manager_with_zone(
|
||||||
host: &str,
|
host: &str,
|
||||||
@@ -296,13 +296,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn dns_peer_info_try_from_invalid_zone_rejected() {
|
fn dns_peer_info_try_from_invalid_zone_rejected() {
|
||||||
let cfg = DnsExportConfig {
|
let cfg = DnsExportConfig {
|
||||||
zones: vec![ZoneData::new(
|
zones: vec![ZoneData::new(&".".parse().unwrap(), 60, ["?"], [], [])],
|
||||||
&".".parse().unwrap(),
|
|
||||||
60,
|
|
||||||
["?"],
|
|
||||||
Vec::<Url>::new(),
|
|
||||||
false,
|
|
||||||
)],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(DnsPeerInfo::try_from(cfg).is_err());
|
assert!(DnsPeerInfo::try_from(cfg).is_err());
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ use hickory_proto::rr;
|
|||||||
use hickory_proto::rr::{DNSClass, Name, RData, RecordType};
|
use hickory_proto::rr::{DNSClass, Name, RData, RecordType};
|
||||||
use hickory_proto::serialize::binary::{BinEncodable, BinEncoder};
|
use hickory_proto::serialize::binary::{BinEncodable, BinEncoder};
|
||||||
use hickory_server::server::Request;
|
use hickory_server::server::Request;
|
||||||
|
use maplit::hashset;
|
||||||
use tokio::sync::Notify;
|
use tokio::sync::Notify;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -108,10 +109,8 @@ pub fn zone_data_a_with_forwarders(origin: &str, record: &str, forwarders: Vec<&
|
|||||||
&origin.parse().unwrap(),
|
&origin.parse().unwrap(),
|
||||||
60,
|
60,
|
||||||
[format!("@ IN A {record}")],
|
[format!("@ IN A {record}")],
|
||||||
forwarders
|
forwarders.into_iter().map(|f| Url::from_str(f).unwrap()),
|
||||||
.into_iter()
|
hashset! {},
|
||||||
.map(|f| Url::from_str(f).expect("invalid forwarder")),
|
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,31 +39,19 @@ impl From<(IpAddr, &ConnectionConfig)> for NameServerAddr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&NameServerAddr> for Url {
|
|
||||||
fn from(value: &NameServerAddr) -> Self {
|
|
||||||
Url::parse(&format!("{}://{}", value.protocol, value.addr)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NameServerAddr> for Url {
|
|
||||||
fn from(value: NameServerAddr) -> Self {
|
|
||||||
(&value).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&Url> for NameServerAddr {
|
impl TryFrom<&Url> for NameServerAddr {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(value: &Url) -> Result<Self, Self::Error> {
|
fn try_from(url: &Url) -> Result<Self, Self::Error> {
|
||||||
let protocol = match Protocol::deserialize(value.scheme().into_deserializer()).map_err(
|
let protocol = match Protocol::deserialize(url.scheme().into_deserializer())
|
||||||
|e: de::value::Error| anyhow!("invalid protocol '{}': {}", value.scheme(), e),
|
.map_err(|e: de::value::Error| anyhow!("invalid protocol '{}': {}", url.scheme(), e))?
|
||||||
)? {
|
{
|
||||||
Protocol::Udp => ProtocolConfig::Udp,
|
Protocol::Udp => ProtocolConfig::Udp,
|
||||||
Protocol::Tcp => ProtocolConfig::Tcp,
|
Protocol::Tcp => ProtocolConfig::Tcp,
|
||||||
p => return Err(anyhow!("unsupported protocol: {}", p)),
|
p => return Err(anyhow!("unsupported protocol: {}", p)),
|
||||||
};
|
};
|
||||||
let host = value.host_str().ok_or(anyhow!("host not found"))?;
|
let host = url.host_str().ok_or(anyhow!("host not found"))?;
|
||||||
let port = value.port().unwrap_or(protocol.default_port());
|
let port = url.port().unwrap_or(protocol.default_port());
|
||||||
let addr = if let Ok(addr) = IpAddr::from_str(host) {
|
let addr = if let Ok(addr) = IpAddr::from_str(host) {
|
||||||
SocketAddr::new(addr, port)
|
SocketAddr::new(addr, port)
|
||||||
} else {
|
} else {
|
||||||
@@ -76,17 +64,35 @@ impl TryFrom<&Url> for NameServerAddr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<NameServerAddr> for proto::common::Url {
|
|
||||||
fn from(value: NameServerAddr) -> Self {
|
|
||||||
Url::from(value).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&proto::common::Url> for NameServerAddr {
|
impl TryFrom<&proto::common::Url> for NameServerAddr {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(value: &proto::common::Url) -> Result<Self, Self::Error> {
|
fn try_from(value: &proto::common::Url) -> Result<Self, Self::Error> {
|
||||||
Self::try_from(&Url::try_from(value)?)
|
(&Url::try_from(value)?).try_into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&NameServerAddr> for Url {
|
||||||
|
fn from(value: &NameServerAddr) -> Self {
|
||||||
|
Url::parse(&format!("{}://{}", value.protocol, value.addr)).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&NameServerAddr> for proto::common::Url {
|
||||||
|
fn from(value: &NameServerAddr) -> Self {
|
||||||
|
Url::from(value).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NameServerAddr> for Url {
|
||||||
|
fn from(value: NameServerAddr) -> Self {
|
||||||
|
(&value).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NameServerAddr> for proto::common::Url {
|
||||||
|
fn from(value: NameServerAddr) -> Self {
|
||||||
|
(&value).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,27 @@
|
|||||||
|
use crate::dns::config::zone::Fallthrough;
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
use derive_more::{Deref, DerefMut, From};
|
use derive_more::{Constructor, Deref, DerefMut};
|
||||||
use hickory_proto::op::ResponseCode;
|
use hickory_proto::op::ResponseCode;
|
||||||
use hickory_proto::rr::{LowerName, RecordType, TSigResponseContext};
|
use hickory_proto::rr::{LowerName, RecordType, TSigResponseContext};
|
||||||
use hickory_server::server::{Request, RequestInfo};
|
use hickory_server::server::{Request, RequestInfo};
|
||||||
use hickory_server::zone_handler::{
|
use hickory_server::zone_handler::{
|
||||||
AuthLookup, AxfrPolicy, LookupControlFlow, LookupError, LookupOptions, ZoneHandler, ZoneType,
|
AuthLookup, AxfrPolicy, LookupControlFlow, LookupError, LookupOptions, ZoneHandler, ZoneType,
|
||||||
};
|
};
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub type ArcZoneHandler = Arc<dyn ZoneHandler>;
|
pub type ArcZoneHandler = Arc<dyn ZoneHandler>;
|
||||||
|
|
||||||
pub trait LookupControlFlowExt {
|
#[derive(Constructor, Deref, DerefMut)]
|
||||||
fn skip_negative(self) -> Self;
|
pub struct ChainedZoneHandler<H>
|
||||||
}
|
|
||||||
|
|
||||||
impl LookupControlFlowExt for LookupControlFlow<AuthLookup> {
|
|
||||||
fn skip_negative(self) -> Self {
|
|
||||||
match self {
|
|
||||||
Self::Continue(e) | Self::Break(e) if matches!(e, Err(LookupError::NameExists)) => {
|
|
||||||
Self::Continue(Ok(Default::default()))
|
|
||||||
}
|
|
||||||
Self::Continue(Err(_)) | Self::Break(Err(_)) => Self::Skip,
|
|
||||||
other => other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(From, Deref, DerefMut)]
|
|
||||||
pub struct ChainedZoneHandler<H>(H)
|
|
||||||
where
|
where
|
||||||
H: ZoneHandler;
|
H: ZoneHandler,
|
||||||
|
{
|
||||||
|
#[deref]
|
||||||
|
#[deref_mut]
|
||||||
|
handler: H,
|
||||||
|
fallthrough: HashSet<Fallthrough>,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<H> ZoneHandler for ChainedZoneHandler<H>
|
impl<H> ZoneHandler for ChainedZoneHandler<H>
|
||||||
@@ -37,7 +29,7 @@ where
|
|||||||
H: ZoneHandler,
|
H: ZoneHandler,
|
||||||
{
|
{
|
||||||
delegate! {
|
delegate! {
|
||||||
to self.0 {
|
to self.handler {
|
||||||
fn zone_type(&self) -> ZoneType;
|
fn zone_type(&self) -> ZoneType;
|
||||||
fn axfr_policy(&self) -> AxfrPolicy;
|
fn axfr_policy(&self) -> AxfrPolicy;
|
||||||
fn origin(&self) -> &LowerName;
|
fn origin(&self) -> &LowerName;
|
||||||
@@ -50,7 +42,7 @@ where
|
|||||||
update: &Request,
|
update: &Request,
|
||||||
now: u64,
|
now: u64,
|
||||||
) -> (Result<bool, ResponseCode>, Option<TSigResponseContext>) {
|
) -> (Result<bool, ResponseCode>, Option<TSigResponseContext>) {
|
||||||
self.0.update(update, now).await
|
self.handler.update(update, now).await
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
async fn lookup(
|
async fn lookup(
|
||||||
@@ -60,29 +52,9 @@ where
|
|||||||
request_info: Option<&RequestInfo<'_>>,
|
request_info: Option<&RequestInfo<'_>>,
|
||||||
lookup_options: LookupOptions,
|
lookup_options: LookupOptions,
|
||||||
) -> LookupControlFlow<AuthLookup> {
|
) -> LookupControlFlow<AuthLookup> {
|
||||||
self.0
|
self.handler
|
||||||
.lookup(name, rtype, request_info, lookup_options)
|
.lookup(name, rtype, request_info, lookup_options)
|
||||||
.await
|
.await
|
||||||
.skip_negative()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
async fn consult(
|
|
||||||
&self,
|
|
||||||
name: &LowerName,
|
|
||||||
rtype: RecordType,
|
|
||||||
request_info: Option<&RequestInfo<'_>>,
|
|
||||||
lookup_options: LookupOptions,
|
|
||||||
last_result: LookupControlFlow<AuthLookup>,
|
|
||||||
) -> (LookupControlFlow<AuthLookup>, Option<TSigResponseContext>) {
|
|
||||||
let result = if let Some(Ok(l)) = last_result.map_result() {
|
|
||||||
LookupControlFlow::Break(Ok(l))
|
|
||||||
} else {
|
|
||||||
self.0
|
|
||||||
.lookup(name, rtype, request_info, lookup_options)
|
|
||||||
.await
|
|
||||||
.skip_negative()
|
|
||||||
};
|
|
||||||
(result, None)
|
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
async fn search(
|
async fn search(
|
||||||
@@ -90,8 +62,17 @@ where
|
|||||||
request: &Request,
|
request: &Request,
|
||||||
lookup_options: LookupOptions,
|
lookup_options: LookupOptions,
|
||||||
) -> (LookupControlFlow<AuthLookup>, Option<TSigResponseContext>) {
|
) -> (LookupControlFlow<AuthLookup>, Option<TSigResponseContext>) {
|
||||||
let (result, tsig) = self.0.search(request, lookup_options).await;
|
let (result, tsig) = self.handler.search(request, lookup_options).await;
|
||||||
(result.skip_negative(), tsig)
|
|
||||||
|
match &result {
|
||||||
|
LookupControlFlow::Continue(Err(e)) | LookupControlFlow::Break(Err(e))
|
||||||
|
if self.fallthrough.contains(&Fallthrough::Any)
|
||||||
|
|| matches!(e, LookupError::ResponseCode(c) if self.fallthrough.contains(&(*c).into())) =>
|
||||||
|
{
|
||||||
|
(LookupControlFlow::Skip, None)
|
||||||
|
}
|
||||||
|
_ => (result, tsig),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
async fn nsec_records(
|
async fn nsec_records(
|
||||||
@@ -99,6 +80,6 @@ where
|
|||||||
name: &LowerName,
|
name: &LowerName,
|
||||||
lookup_options: LookupOptions,
|
lookup_options: LookupOptions,
|
||||||
) -> LookupControlFlow<AuthLookup> {
|
) -> LookupControlFlow<AuthLookup> {
|
||||||
self.0.nsec_records(name, lookup_options).await
|
self.handler.nsec_records(name, lookup_options).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-45
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::dns::config::zone::Fallthrough;
|
||||||
use crate::dns::utils::addr::{NameServerAddr, NameServerAddrGroup};
|
use crate::dns::utils::addr::{NameServerAddr, NameServerAddrGroup};
|
||||||
use crate::dns::utils::zone_handler::{ArcZoneHandler, ChainedZoneHandler};
|
use crate::dns::utils::zone_handler::{ArcZoneHandler, ChainedZoneHandler};
|
||||||
use crate::proto::dns::ZoneData;
|
use crate::proto::dns::ZoneData;
|
||||||
@@ -11,16 +12,16 @@ use hickory_server::store::in_memory::InMemoryZoneHandler;
|
|||||||
use hickory_server::zone_handler::{AxfrPolicy, ZoneType};
|
use hickory_server::zone_handler::{AxfrPolicy, ZoneType};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use itertools::chain;
|
use itertools::chain;
|
||||||
use std::collections::BTreeMap;
|
use maplit::hashset;
|
||||||
|
use std::collections::{BTreeMap, HashSet};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Zone {
|
pub struct Zone {
|
||||||
origin: LowerName,
|
origin: LowerName,
|
||||||
records: BTreeMap<RrKey, RecordSet>,
|
records: BTreeMap<RrKey, RecordSet>,
|
||||||
pub forward: Option<ForwardConfig>,
|
pub forward: Option<ForwardConfig>,
|
||||||
fallthrough: bool,
|
fallthrough: HashSet<Fallthrough>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zone {
|
impl Zone {
|
||||||
@@ -30,23 +31,16 @@ impl Zone {
|
|||||||
name_servers: config.name_servers().to_vec(),
|
name_servers: config.name_servers().to_vec(),
|
||||||
options: Some(opts),
|
options: Some(opts),
|
||||||
};
|
};
|
||||||
let mut zone = Self::new(".".parse().unwrap());
|
Self {
|
||||||
zone.forward = Some(forward);
|
origin: ".".parse().unwrap(),
|
||||||
zone.fallthrough = false;
|
forward: Some(forward),
|
||||||
zone
|
fallthrough: hashset! {},
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zone {
|
impl Zone {
|
||||||
pub fn new(name: LowerName) -> Self {
|
|
||||||
Self {
|
|
||||||
origin: name,
|
|
||||||
records: BTreeMap::new(),
|
|
||||||
forward: None,
|
|
||||||
fallthrough: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_memory_zone_handler(&self) -> Option<ArcZoneHandler> {
|
pub fn create_memory_zone_handler(&self) -> Option<ArcZoneHandler> {
|
||||||
(!self.records.is_empty()).then(|| {
|
(!self.records.is_empty()).then(|| {
|
||||||
let mut memory = InMemoryZoneHandler::<TokioRuntimeProvider>::empty(
|
let mut memory = InMemoryZoneHandler::<TokioRuntimeProvider>::empty(
|
||||||
@@ -62,11 +56,7 @@ impl Zone {
|
|||||||
.map(|(k, v)| (k, Arc::new(v))),
|
.map(|(k, v)| (k, Arc::new(v))),
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.fallthrough {
|
Arc::new(ChainedZoneHandler::new(memory, self.fallthrough.clone())) as _
|
||||||
Arc::new(ChainedZoneHandler::from(memory)) as _
|
|
||||||
} else {
|
|
||||||
Arc::new(memory) as _
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,14 +67,10 @@ impl Zone {
|
|||||||
TokioRuntimeProvider::default(),
|
TokioRuntimeProvider::default(),
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
.inspect_err(|e| tracing::error!("failed to create forward zone_handler: {:?}", e))
|
.inspect_err(|error| tracing::error!(?error, "failed to create forward zone_handler"))
|
||||||
.ok()
|
.ok()
|
||||||
.map(|f| {
|
.map(|handler| {
|
||||||
if self.fallthrough {
|
Arc::new(ChainedZoneHandler::new(handler, self.fallthrough.clone())) as _
|
||||||
Arc::new(ChainedZoneHandler::from(f)) as _
|
|
||||||
} else {
|
|
||||||
Arc::new(f) as _
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -101,7 +87,7 @@ impl TryFrom<&ZoneData> for Zone {
|
|||||||
let name_servers = value
|
let name_servers = value
|
||||||
.forwarders
|
.forwarders
|
||||||
.iter()
|
.iter()
|
||||||
.map(TryInto::<NameServerAddr>::try_into)
|
.map(NameServerAddr::try_from)
|
||||||
.map(|a| a.map(Into::into))
|
.map(|a| a.map(Into::into))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let forward = (!name_servers.is_empty()).then_some(ForwardConfig {
|
let forward = (!name_servers.is_empty()).then_some(ForwardConfig {
|
||||||
@@ -109,11 +95,13 @@ impl TryFrom<&ZoneData> for Zone {
|
|||||||
options: None,
|
options: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let fallthrough = value.fallthrough.iter().copied().map(Into::into).collect();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
origin: origin.into(),
|
origin: origin.into(),
|
||||||
records,
|
records,
|
||||||
forward,
|
forward,
|
||||||
fallthrough: value.fallthrough,
|
fallthrough,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,7 +120,7 @@ impl From<Zone> for ZoneData {
|
|||||||
.flat_map(|f| f.name_servers.into_iter())
|
.flat_map(|f| f.name_servers.into_iter())
|
||||||
.map(|ns| (&ns).into())
|
.map(|ns| (&ns).into())
|
||||||
.flat_map(NameServerAddrGroup::into_iter)
|
.flat_map(NameServerAddrGroup::into_iter)
|
||||||
.map(Url::from);
|
.map(Into::into);
|
||||||
|
|
||||||
Self::new(&value.origin, 0, records, forwarders, value.fallthrough)
|
Self::new(&value.origin, 0, records, forwarders, value.fallthrough)
|
||||||
}
|
}
|
||||||
@@ -169,7 +157,9 @@ mod tests {
|
|||||||
use hickory_proto::rr::{RData, Record, RecordType, RrsetRecords};
|
use hickory_proto::rr::{RData, Record, RecordType, RrsetRecords};
|
||||||
use hickory_server::Server;
|
use hickory_server::Server;
|
||||||
use hickory_server::zone_handler::Catalog;
|
use hickory_server::zone_handler::Catalog;
|
||||||
use std::net::{Ipv4Addr, SocketAddr};
|
use maplit::hashset;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use tokio::net::UdpSocket;
|
use tokio::net::UdpSocket;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
@@ -193,21 +183,21 @@ mod tests {
|
|||||||
origin: &str,
|
origin: &str,
|
||||||
records: Vec<&str>,
|
records: Vec<&str>,
|
||||||
forwarders: Vec<&str>,
|
forwarders: Vec<&str>,
|
||||||
fallthrough: bool,
|
fallthrough: HashSet<Fallthrough>,
|
||||||
) -> ZoneData {
|
) -> ZoneData {
|
||||||
ZoneData::new(
|
ZoneData::new(
|
||||||
&origin.parse().unwrap(),
|
&origin.parse().unwrap(),
|
||||||
60,
|
60,
|
||||||
records,
|
records,
|
||||||
forwarders.into_iter().map(|url| Url {
|
forwarders
|
||||||
url: url.to_string(),
|
.into_iter()
|
||||||
}),
|
.map(|url| Url::from_str(url).unwrap()),
|
||||||
fallthrough,
|
fallthrough,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zone_data(origin: &str, records: Vec<&str>, forwarders: Vec<&str>) -> ZoneData {
|
fn zone_data(origin: &str, records: Vec<&str>, forwarders: Vec<&str>) -> ZoneData {
|
||||||
zone_data_with_fallthrough(origin, records, forwarders, true)
|
zone_data_with_fallthrough(origin, records, forwarders, hashset! {Fallthrough::Any})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_catalog(zones: ZoneGroup) -> Catalog {
|
fn build_catalog(zones: ZoneGroup) -> Catalog {
|
||||||
@@ -242,6 +232,13 @@ mod tests {
|
|||||||
.any(|record| matches!(record.data, RData::A(addr) if *addr == expected))
|
.any(|record| matches!(record.data, RData::A(addr) if *addr == expected))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_aaaa_answer(message: &Message, expected: Ipv6Addr) -> bool {
|
||||||
|
message
|
||||||
|
.answers
|
||||||
|
.iter()
|
||||||
|
.any(|record| matches!(record.data, RData::AAAA(addr) if *addr == expected))
|
||||||
|
}
|
||||||
|
|
||||||
async fn start_upstream_server() -> anyhow::Result<(SocketAddr, JoinHandle<()>)> {
|
async fn start_upstream_server() -> anyhow::Result<(SocketAddr, JoinHandle<()>)> {
|
||||||
let upstream = Zone::try_from(&zone_data(
|
let upstream = Zone::try_from(&zone_data(
|
||||||
"upstream.test",
|
"upstream.test",
|
||||||
@@ -400,7 +397,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn catalog_lookup_returns_nodata_on_nameexists() -> anyhow::Result<()> {
|
async fn catalog_lookup_forwards_on_nameexists() -> anyhow::Result<()> {
|
||||||
let upstream = Zone::try_from(&zone_data(
|
let upstream = Zone::try_from(&zone_data(
|
||||||
"forward-aaaa.test",
|
"forward-aaaa.test",
|
||||||
vec!["host 60 IN AAAA 2001:db8::1"],
|
vec!["host 60 IN AAAA 2001:db8::1"],
|
||||||
@@ -435,10 +432,7 @@ mod tests {
|
|||||||
assert_eq!(rcode, ResponseCode::NoError);
|
assert_eq!(rcode, ResponseCode::NoError);
|
||||||
|
|
||||||
let message = message.expect("response should exist");
|
let message = message.expect("response should exist");
|
||||||
assert!(
|
assert!(has_aaaa_answer(&message, "2001:db8::1".parse()?));
|
||||||
message.answers.is_empty(),
|
|
||||||
"NameExists should return NODATA"
|
|
||||||
);
|
|
||||||
|
|
||||||
upstream_handle.abort();
|
upstream_handle.abort();
|
||||||
let _ = upstream_handle.await;
|
let _ = upstream_handle.await;
|
||||||
@@ -528,13 +522,13 @@ mod tests {
|
|||||||
"fallback-disabled.test",
|
"fallback-disabled.test",
|
||||||
vec!["first IN A 10.20.31.1"],
|
vec!["first IN A 10.20.31.1"],
|
||||||
vec![],
|
vec![],
|
||||||
false,
|
hashset! {},
|
||||||
))?,
|
))?,
|
||||||
Zone::try_from(&zone_data_with_fallthrough(
|
Zone::try_from(&zone_data_with_fallthrough(
|
||||||
"fallback-disabled.test",
|
"fallback-disabled.test",
|
||||||
vec!["target IN A 10.20.31.2"],
|
vec!["target IN A 10.20.31.2"],
|
||||||
vec![],
|
vec![],
|
||||||
false,
|
hashset! {},
|
||||||
))?,
|
))?,
|
||||||
]
|
]
|
||||||
.into();
|
.into();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package dns;
|
|||||||
message ZoneData {
|
message ZoneData {
|
||||||
string content = 1;
|
string content = 1;
|
||||||
repeated common.Url forwarders = 2;
|
repeated common.Url forwarders = 2;
|
||||||
bool fallthrough = 3;
|
repeated int32 fallthrough = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetExportConfigRequest {}
|
message GetExportConfigRequest {}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::dns::config::zone::Fallthrough;
|
||||||
use crate::proto::common::Url;
|
use crate::proto::common::Url;
|
||||||
use crate::proto::utils::TransientDigest;
|
use crate::proto::utils::TransientDigest;
|
||||||
use hickory_proto::rr::LowerName;
|
use hickory_proto::rr::LowerName;
|
||||||
@@ -13,19 +14,13 @@ impl HeartbeatRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ZoneData {
|
impl ZoneData {
|
||||||
pub fn new<Records, R, Urls, U>(
|
pub fn new<Record: AsRef<str>>(
|
||||||
origin: &LowerName,
|
origin: &LowerName,
|
||||||
ttl: u32,
|
ttl: u32,
|
||||||
records: Records,
|
records: impl IntoIterator<Item = Record>,
|
||||||
forwarders: Urls,
|
forwarders: impl IntoIterator<Item = Url>,
|
||||||
fallthrough: bool,
|
fallthrough: impl IntoIterator<Item = Fallthrough>,
|
||||||
) -> Self
|
) -> Self {
|
||||||
where
|
|
||||||
Records: IntoIterator<Item = R>,
|
|
||||||
R: AsRef<str>,
|
|
||||||
Urls: IntoIterator<Item = U>,
|
|
||||||
U: Into<Url>,
|
|
||||||
{
|
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
|
|
||||||
content.push_str("; EasyTier Magic DNS zone data\n");
|
content.push_str("; EasyTier Magic DNS zone data\n");
|
||||||
@@ -44,7 +39,8 @@ impl ZoneData {
|
|||||||
content.push('\n');
|
content.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
let forwarders = forwarders.into_iter().map(Into::into).collect();
|
let forwarders = forwarders.into_iter().collect();
|
||||||
|
let fallthrough = fallthrough.into_iter().map(Into::into).collect();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
content,
|
content,
|
||||||
|
|||||||
Reference in New Issue
Block a user