zone: move chained authority to utils, rewrite authority creation

This commit is contained in:
Luna Yao
2026-02-19 16:26:13 +01:00
parent 77ccd7dbc9
commit a6bec0fa6f
2 changed files with 129 additions and 117 deletions
+91 -1
View File
@@ -1,7 +1,13 @@
use anyhow::{anyhow, Error};
use hickory_proto::rr::LowerName;
use derive_more::{Deref, DerefMut};
use hickory_proto::rr::{LowerName, RecordType};
use hickory_proto::xfer::Protocol;
use hickory_resolver::config::NameServerConfig;
use hickory_server::authority::{
Authority, LookupControlFlow, LookupObject, LookupOptions, MessageRequest, UpdateResult,
ZoneType,
};
use hickory_server::server::RequestInfo;
use idna::AsciiDenyList;
use serde_with::{DeserializeFromStr, SerializeDisplay};
use std::fmt::{Display, Formatter};
@@ -121,3 +127,87 @@ impl Display for NameServerAddr {
f.write_str(Url::from(self).as_str())
}
}
#[derive(Deref, DerefMut)]
pub struct ChainedAuthority<A>(pub(super) A)
where
A: Authority,
A::Lookup: LookupObject + 'static;
impl<A> From<A> for ChainedAuthority<A>
where
A: Authority,
A::Lookup: LookupObject + 'static,
{
fn from(value: A) -> Self {
Self(value)
}
}
#[async_trait::async_trait]
impl<A> Authority for ChainedAuthority<A>
where
A: Authority,
A::Lookup: LookupObject + 'static,
{
type Lookup = A::Lookup;
#[inline]
fn zone_type(&self) -> ZoneType {
self.0.zone_type()
}
#[inline]
fn is_axfr_allowed(&self) -> bool {
self.0.is_axfr_allowed()
}
#[inline]
async fn update(&self, update: &MessageRequest) -> UpdateResult<bool> {
self.0.update(update).await
}
#[inline]
fn origin(&self) -> &LowerName {
self.0.origin()
}
#[inline]
async fn lookup(
&self,
name: &LowerName,
rtype: RecordType,
lookup_options: LookupOptions,
) -> LookupControlFlow<Self::Lookup> {
self.0.lookup(name, rtype, lookup_options).await
}
#[inline]
async fn consult(
&self,
name: &LowerName,
rtype: RecordType,
lookup_options: LookupOptions,
last_result: LookupControlFlow<Box<dyn LookupObject>>,
) -> LookupControlFlow<Box<dyn LookupObject>> {
if let Some(Ok(l)) = last_result.map_result() {
LookupControlFlow::Break(Ok(l))
} else {
self.0
.lookup(name, rtype, lookup_options)
.await
.map(|l| Box::new(l) as _)
}
}
#[inline]
async fn search(
&self,
request_info: RequestInfo<'_>,
lookup_options: LookupOptions,
) -> LookupControlFlow<Self::Lookup> {
self.0.search(request_info, lookup_options).await
}
#[inline]
async fn get_nsec_records(
&self,
name: &LowerName,
lookup_options: LookupOptions,
) -> LookupControlFlow<Self::Lookup> {
self.0.get_nsec_records(name, lookup_options).await
}
}
+38 -116
View File
@@ -1,104 +1,15 @@
use crate::dns::utils::NameServerAddr;
use crate::dns::utils::{ChainedAuthority, NameServerAddr};
use crate::proto::dns::ZoneConfigPb;
use async_trait::async_trait;
use derive_more::{Deref, DerefMut};
use hickory_proto::rr::{LowerName, Record, RecordSet, RecordType, RrKey, RrsetRecords};
use hickory_proto::rr::{LowerName, Record, RecordSet, RrKey, RrsetRecords};
use hickory_proto::serialize::txt::Parser;
use hickory_proto::xfer::Protocol;
use hickory_resolver::config::{NameServerConfig, ResolverOpts};
use hickory_resolver::config::ResolverOpts;
use hickory_resolver::name_server::TokioConnectionProvider;
use hickory_server::authority::{
Authority, AuthorityObject, LookupControlFlow, LookupObject, LookupOptions, MessageRequest,
UpdateResult, ZoneType,
};
use hickory_server::server::RequestInfo;
use hickory_server::authority::ZoneType;
use hickory_server::store::forwarder::{ForwardAuthority, ForwardConfig};
use hickory_server::store::in_memory::InMemoryAuthority;
use std::collections::BTreeMap;
use std::mem;
use std::net::{IpAddr, SocketAddr};
use std::str::FromStr;
use std::sync::Arc;
use url::Url;
#[derive(Deref, DerefMut)]
pub struct FallbackAuthority<A, L>
where
A: Authority<Lookup = L> + Send + Sync + 'static,
L: LookupObject + Send + Sync + 'static,
{
#[deref]
#[deref_mut]
inner: A,
}
#[async_trait]
impl<A, L> Authority for FallbackAuthority<A, L>
where
A: Authority<Lookup = L> + Send + Sync + 'static,
L: LookupObject + Send + Sync + 'static,
{
type Lookup = L;
#[inline]
fn zone_type(&self) -> ZoneType {
self.inner.zone_type()
}
#[inline]
fn is_axfr_allowed(&self) -> bool {
self.inner.is_axfr_allowed()
}
#[inline]
async fn update(&self, update: &MessageRequest) -> UpdateResult<bool> {
self.inner.update(update).await
}
#[inline]
fn origin(&self) -> &LowerName {
self.inner.origin()
}
#[inline]
async fn lookup(
&self,
name: &LowerName,
rtype: RecordType,
lookup_options: LookupOptions,
) -> LookupControlFlow<Self::Lookup> {
self.inner.lookup(name, rtype, lookup_options).await
}
#[inline]
async fn consult(
&self,
name: &LowerName,
rtype: RecordType,
lookup_options: LookupOptions,
last_result: LookupControlFlow<Box<dyn LookupObject>>,
) -> LookupControlFlow<Box<dyn LookupObject>> {
if let Some(Ok(l)) = last_result.map_result() {
LookupControlFlow::Break(Ok(l))
} else {
self.inner
.lookup(name, rtype, lookup_options)
.await
.map(|l| Box::new(l) as _)
}
}
#[inline]
async fn search(
&self,
request_info: RequestInfo<'_>,
lookup_options: LookupOptions,
) -> LookupControlFlow<Self::Lookup> {
self.inner.search(request_info, lookup_options).await
}
#[inline]
async fn get_nsec_records(
&self,
name: &LowerName,
lookup_options: LookupOptions,
) -> LookupControlFlow<Self::Lookup> {
self.inner.get_nsec_records(name, lookup_options).await
}
}
#[derive(Debug, Clone)]
pub struct Zone {
@@ -116,34 +27,35 @@ impl Zone {
}
}
pub fn create_authorities(&self) -> anyhow::Result<Vec<Arc<dyn AuthorityObject>>> {
let mut authorities = Vec::<Arc<dyn AuthorityObject>>::with_capacity(2);
pub fn create_memory_authority(&self) -> Option<Arc<ChainedAuthority<InMemoryAuthority>>> {
(!self.records.is_empty()).then(|| {
let mut memory =
InMemoryAuthority::empty(self.origin.clone().into(), ZoneType::External, false);
let mut memory =
InMemoryAuthority::empty(self.origin.clone().into(), ZoneType::External, false);
memory.records_get_mut().extend(
self.records
.clone()
.into_iter()
.map(|(k, v)| (k, Arc::new(v))),
);
let mut records = self
.records
.clone()
.into_iter()
.map(|(k, v)| (k, Arc::new(v)))
.collect();
mem::swap(memory.records_get_mut(), &mut records);
Arc::new(memory.into())
})
}
authorities.push(Arc::new(memory));
if let Some(forward) = &self.forward {
let forward = ForwardAuthority::builder_with_config(
pub fn create_forward_authority(
&self,
) -> Option<Arc<ChainedAuthority<ForwardAuthority<TokioConnectionProvider>>>> {
self.forward.as_ref().and_then(|forward| {
ForwardAuthority::builder_with_config(
forward.clone(),
TokioConnectionProvider::default(),
)
.build()
.map_err(|e| anyhow::anyhow!("failed to create forward authority: {}", e))?;
let forward = FallbackAuthority { inner: forward };
authorities.push(Arc::new(forward));
}
Ok(authorities)
.inspect_err(|e| tracing::error!("failed to create forward authority: {:?}", e))
.ok()
.map(|f| Arc::new(f.into()))
})
}
pub fn set_forwarders<F, A>(
@@ -215,7 +127,7 @@ mod tests {
use hickory_proto::rr::{rdata, DNSClass, Name, RData, RecordType};
use hickory_proto::runtime::TokioRuntimeProvider;
use hickory_proto::udp::UdpClientStream;
use hickory_server::authority::Catalog;
use hickory_server::authority::{AuthorityObject, Catalog};
use hickory_server::ServerFuture;
use std::time::Duration;
use tokio::net::UdpSocket;
@@ -334,7 +246,17 @@ mod tests {
assert_eq!(zone.forward.as_ref().unwrap().name_servers.len(), 1);
let authorities = zone.create_authorities()?;
let mut authorities = Vec::new();
authorities.extend(
zone.create_memory_authority()
.map(|a| a as Arc<dyn AuthorityObject>)
.into_iter(),
);
authorities.extend(
zone.create_forward_authority()
.map(|a| a as Arc<dyn AuthorityObject>)
.into_iter(),
);
assert_eq!(authorities.len(), 2);
let mut catalog = Catalog::new();
catalog.upsert(zone.origin.clone().into(), authorities);