use std::io;
use tracing::{debug, info};
use crate::{
authority::{
Authority, LookupError, LookupObject, LookupOptions, MessageRequest, UpdateResult, ZoneType,
},
client::{
op::ResponseCode,
rr::{LowerName, Name, Record, RecordType},
},
resolver::{
config::ResolverConfig, lookup::Lookup as ResolverLookup, TokioAsyncResolver, TokioHandle,
},
server::RequestInfo,
store::forwarder::ForwardConfig,
};
pub struct ForwardAuthority {
origin: LowerName,
resolver: TokioAsyncResolver,
}
impl ForwardAuthority {
#[allow(clippy::new_without_default)]
#[doc(hidden)]
pub fn new(runtime: TokioHandle) -> Result<Self, String> {
let resolver = TokioAsyncResolver::from_system_conf(runtime)
.map_err(|e| format!("error constructing new Resolver: {}", e))?;
Ok(Self {
origin: Name::root().into(),
resolver,
})
}
pub fn try_from_config(
origin: Name,
_zone_type: ZoneType,
config: &ForwardConfig,
) -> Result<Self, String> {
info!("loading forwarder config: {}", origin);
let name_servers = config.name_servers.clone();
let mut options = config.options.unwrap_or_default();
if !options.preserve_intermediates {
tracing::warn!(
"preserve_intermediates set to false, which is invalid \
for a forwarder; switching to true"
);
options.preserve_intermediates = true;
}
let config = ResolverConfig::from_parts(None, vec![], name_servers);
let resolver = TokioAsyncResolver::new(config, options, TokioHandle)
.map_err(|e| format!("error constructing new Resolver: {}", e))?;
info!("forward resolver configured: {}: ", origin);
Ok(Self {
origin: origin.into(),
resolver,
})
}
}
#[async_trait::async_trait]
impl Authority for ForwardAuthority {
type Lookup = ForwardLookup;
fn zone_type(&self) -> ZoneType {
ZoneType::Forward
}
fn is_axfr_allowed(&self) -> bool {
false
}
async fn update(&self, _update: &MessageRequest) -> UpdateResult<bool> {
Err(ResponseCode::NotImp)
}
fn origin(&self) -> &LowerName {
&self.origin
}
async fn lookup(
&self,
name: &LowerName,
rtype: RecordType,
_lookup_options: LookupOptions,
) -> Result<Self::Lookup, LookupError> {
debug_assert!(self.origin.zone_of(name));
debug!("forwarding lookup: {} {}", name, rtype);
let name: LowerName = name.clone();
let resolve = self.resolver.lookup(name, rtype).await;
resolve.map(ForwardLookup).map_err(LookupError::from)
}
async fn search(
&self,
request_info: RequestInfo<'_>,
lookup_options: LookupOptions,
) -> Result<Self::Lookup, LookupError> {
self.lookup(
request_info.query.name(),
request_info.query.query_type(),
lookup_options,
)
.await
}
async fn get_nsec_records(
&self,
_name: &LowerName,
_lookup_options: LookupOptions,
) -> Result<Self::Lookup, LookupError> {
Err(LookupError::from(io::Error::new(
io::ErrorKind::Other,
"Getting NSEC records is unimplemented for the forwarder",
)))
}
}
pub struct ForwardLookup(pub ResolverLookup);
impl LookupObject for ForwardLookup {
fn is_empty(&self) -> bool {
self.0.is_empty()
}
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Record> + Send + 'a> {
Box::new(self.0.record_iter())
}
fn take_additionals(&mut self) -> Option<Box<dyn LookupObject>> {
None
}
}