use std::borrow::Borrow;
use std::collections::BTreeMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use futures_util::future::{self, TryFutureExt};
use log::{debug, error};
use crate::client::op::{LowerQuery, ResponseCode};
use crate::client::rr::dnssec::{DnsSecResult, Signer, SupportedAlgorithms};
use crate::client::rr::rdata::key::KEY;
#[cfg(feature = "dnssec")]
use crate::client::rr::rdata::DNSSECRData;
use crate::client::rr::rdata::DNSSECRecordType;
use crate::client::rr::rdata::SOA;
use crate::client::rr::{DNSClass, LowerName, Name, RData, Record, RecordSet, RecordType, RrKey};
use crate::authority::{
AnyRecords, AuthLookup, Authority, LookupError, LookupRecords, LookupResult, MessageRequest,
UpdateResult, ZoneType,
};
pub struct InMemoryAuthority {
origin: LowerName,
class: DNSClass,
records: BTreeMap<RrKey, Arc<RecordSet>>,
zone_type: ZoneType,
allow_axfr: bool,
secure_keys: Vec<Signer>,
}
impl InMemoryAuthority {
pub fn new(
origin: Name,
records: BTreeMap<RrKey, RecordSet>,
zone_type: ZoneType,
allow_axfr: bool,
) -> Result<Self, String> {
let mut this = Self::empty(origin.clone(), zone_type, allow_axfr);
let serial = records
.iter()
.find(|(key, _)| key.record_type == RecordType::SOA)
.and_then(|(_, rrset)| rrset.records_without_rrsigs().next())
.and_then(|record| record.rdata().as_soa())
.map(SOA::serial)
.ok_or_else(|| format!("SOA record must be present: {}", origin))?;
let iter = records.into_iter().map(|(_key, record)| record);
for rrset in iter {
let name = rrset.name().clone();
let rr_type = rrset.record_type();
for record in rrset.records_without_rrsigs() {
if !this.upsert(record.clone(), serial) {
return Err(format!(
"Failed to insert {} {} to zone: {}",
name, rr_type, origin
));
};
}
}
Ok(this)
}
pub fn empty(origin: Name, zone_type: ZoneType, allow_axfr: bool) -> Self {
Self {
origin: LowerName::new(&origin),
class: DNSClass::IN,
records: BTreeMap::new(),
zone_type,
allow_axfr,
secure_keys: Vec::new(),
}
}
pub fn clear(&mut self) {
self.records.clear()
}
pub fn class(&self) -> DNSClass {
self.class
}
pub fn set_allow_axfr(&mut self, allow_axfr: bool) {
self.allow_axfr = allow_axfr;
}
pub fn secure_keys(&self) -> &[Signer] {
&self.secure_keys
}
pub fn records(&self) -> &BTreeMap<RrKey, Arc<RecordSet>> {
&self.records
}
pub fn records_mut(&mut self) -> &mut BTreeMap<RrKey, Arc<RecordSet>> {
&mut self.records
}
fn inner_soa(&self) -> Option<&SOA> {
let rr_key = RrKey::new(self.origin.clone(), RecordType::SOA);
self.records
.get(&rr_key)
.and_then(|rrset| rrset.records_without_rrsigs().next())
.and_then(|record| record.rdata().as_soa())
}
pub fn minimum_ttl(&self) -> u32 {
let soa = self.inner_soa();
let soa = match soa {
Some(soa) => soa,
None => {
error!("could not lookup SOA for authority: {}", self.origin);
return 0;
}
};
soa.minimum()
}
pub fn serial(&self) -> u32 {
let soa = self.inner_soa();
let soa = match soa {
Some(soa) => soa,
None => {
error!("could not lookup SOA for authority: {}", self.origin);
return 0;
}
};
soa.serial()
}
fn inner_lookup(
&self,
name: &LowerName,
record_type: RecordType,
and_rrsigs: bool,
supported_algorithms: SupportedAlgorithms,
) -> Option<Arc<RecordSet>> {
let start_range_key = RrKey::new(name.clone(), RecordType::Unknown(u16::min_value()));
let end_range_key = RrKey::new(name.clone(), RecordType::Unknown(u16::max_value()));
fn aname_covers_type(key_type: RecordType, query_type: RecordType) -> bool {
(query_type == RecordType::A || query_type == RecordType::AAAA)
&& key_type == RecordType::ANAME
}
let lookup = self
.records
.range(&start_range_key..&end_range_key)
.find(|(key, _)| {
key.record_type == record_type
|| key.record_type == RecordType::CNAME
|| aname_covers_type(key.record_type, record_type)
})
.map(|(_key, rr_set)| rr_set);
match lookup {
None => self.inner_lookup_wildcard(name, record_type, and_rrsigs, supported_algorithms),
l => l.cloned(),
}
}
fn inner_lookup_wildcard(
&self,
name: &LowerName,
record_type: RecordType,
and_rrsigs: bool,
supported_algorithms: SupportedAlgorithms,
) -> Option<Arc<RecordSet>> {
let wildcard = if name.is_wildcard() || name.is_root() {
return None;
} else {
name.clone().into_wildcard()
};
self.inner_lookup(&wildcard, record_type, and_rrsigs, supported_algorithms)
.map(|rrset| {
let mut new_answer =
RecordSet::new(name.borrow(), rrset.record_type(), rrset.ttl());
let (records, _rrsigs): (Vec<&Record>, Vec<&Record>) = rrset
.records(and_rrsigs, supported_algorithms)
.partition(|r| r.record_type() != RecordType::DNSSEC(DNSSECRecordType::RRSIG));
for record in records {
new_answer.add_rdata(record.rdata().clone());
}
#[cfg(feature = "dnssec")]
for rrsig in _rrsigs {
new_answer.insert_rrsig(rrsig.clone())
}
Arc::new(new_answer)
})
}
fn additional_search(
&self,
query_type: RecordType,
next_name: LowerName,
_search_type: RecordType,
and_rrsigs: bool,
supported_algorithms: SupportedAlgorithms,
) -> Option<Vec<Arc<RecordSet>>> {
let mut additionals: Vec<Arc<RecordSet>> = vec![];
let mut query_types_arr = [query_type; 2];
let query_types: &[RecordType] = match query_type {
RecordType::ANAME | RecordType::NS | RecordType::MX | RecordType::SRV => {
query_types_arr = [RecordType::A, RecordType::AAAA];
&query_types_arr[..]
}
_ => &query_types_arr[..1],
};
for query_type in query_types {
let mut next_name = Some(next_name.clone());
while let Some(search) = next_name.take() {
let additional =
self.inner_lookup(&search, *query_type, and_rrsigs, supported_algorithms);
let mut continue_name = None;
if let Some(additional) = additional {
if !additionals.contains(&additional) {
additionals.push(additional.clone());
}
continue_name =
maybe_next_name(&additional, *query_type).map(|(name, _search_type)| name);
}
next_name = continue_name;
}
}
if !additionals.is_empty() {
Some(additionals)
} else {
None
}
}
#[cfg(any(feature = "dnssec", feature = "sqlite"))]
pub(crate) fn increment_soa_serial(&mut self) -> u32 {
let rr_key = RrKey::new(self.origin.clone(), RecordType::SOA);
let record = self
.records
.remove(&rr_key)
.and_then(|rrset| rrset.records_without_rrsigs().next().cloned());
let mut record = if let Some(record) = record {
record
} else {
error!("could not lookup SOA for authority: {}", self.origin);
return 0;
};
let serial = if let RData::SOA(ref mut soa_rdata) = *record.rdata_mut() {
soa_rdata.increment_serial();
soa_rdata.serial()
} else {
panic!("This was not an SOA record");
};
self.upsert(record, serial);
serial
}
pub fn upsert(&mut self, record: Record, serial: u32) -> bool {
assert_eq!(self.class, record.dns_class());
#[cfg(feature = "dnssec")]
fn is_nsec(upsert_type: RecordType, occupied_type: RecordType) -> bool {
upsert_type == RecordType::DNSSEC(DNSSECRecordType::NSEC)
|| upsert_type == RecordType::DNSSEC(DNSSECRecordType::NSEC3)
|| occupied_type == RecordType::DNSSEC(DNSSECRecordType::NSEC)
|| occupied_type == RecordType::DNSSEC(DNSSECRecordType::NSEC3)
}
#[cfg(not(feature = "dnssec"))]
fn is_nsec(_upsert_type: RecordType, _occupied_type: RecordType) -> bool {
false
}
#[allow(clippy::nonminimal_bool)]
fn label_does_not_allow_multiple(
upsert_type: RecordType,
occupied_type: RecordType,
check_type: RecordType,
) -> bool {
(upsert_type == check_type && occupied_type != check_type) ||
(upsert_type != check_type && occupied_type == check_type)
}
let start_range_key =
RrKey::new(record.name().into(), RecordType::Unknown(u16::min_value()));
let end_range_key = RrKey::new(record.name().into(), RecordType::Unknown(u16::max_value()));
let multiple_records_at_label_disallowed = self
.records
.range(&start_range_key..&end_range_key)
.any(|(key, _)| {
!is_nsec(record.record_type(), key.record_type)
&& label_does_not_allow_multiple(
record.record_type(),
key.record_type,
RecordType::CNAME,
)
});
if multiple_records_at_label_disallowed {
return false;
}
let rr_key = RrKey::new(record.name().into(), record.rr_type());
let records: &mut Arc<RecordSet> = self
.records
.entry(rr_key)
.or_insert_with(|| Arc::new(RecordSet::new(record.name(), record.rr_type(), serial)));
let mut records_clone = RecordSet::clone(&*records);
if records_clone.insert(record, serial) {
*records = Arc::new(records_clone);
true
} else {
false
}
}
#[cfg(feature = "dnssec")]
pub fn secure_zone(&mut self) -> DnsSecResult<()> {
self.nsec_zone();
self.increment_soa_serial();
self.sign_zone()
}
#[cfg(not(feature = "dnssec"))]
pub fn secure_zone(&mut self) -> Result<(), &str> {
Err("DNSSEC was not enabled during compilation.")
}
#[cfg(feature = "dnssec")]
fn nsec_zone(&mut self) {
use crate::client::rr::rdata::NSEC;
if self.secure_keys.is_empty() {
return;
}
debug!("generating nsec records: {}", self.origin);
let delete_keys: Vec<RrKey> = self
.records
.keys()
.filter(|k| k.record_type == RecordType::DNSSEC(DNSSECRecordType::NSEC))
.cloned()
.collect();
for key in delete_keys {
self.records.remove(&key);
}
let ttl = self.minimum_ttl();
let serial = self.serial();
let mut records: Vec<Record> = vec![];
{
let mut nsec_info: Option<(&Name, Vec<RecordType>)> = None;
for key in self.records.keys() {
match nsec_info {
None => nsec_info = Some((key.name.borrow(), vec![key.record_type])),
Some((name, ref mut vec)) if LowerName::new(name) == key.name => {
vec.push(key.record_type)
}
Some((name, vec)) => {
let mut record = Record::with(
name.clone(),
RecordType::DNSSEC(DNSSECRecordType::NSEC),
ttl,
);
let rdata = NSEC::new_cover_self(key.name.clone().into(), vec);
record.set_rdata(RData::DNSSEC(DNSSECRData::NSEC(rdata)));
records.push(record);
nsec_info = Some((&key.name.borrow(), vec![key.record_type]))
}
}
}
if let Some((name, vec)) = nsec_info {
let mut record = Record::with(
name.clone(),
RecordType::DNSSEC(DNSSECRecordType::NSEC),
ttl,
);
let rdata = NSEC::new_cover_self(Authority::origin(self).clone().into(), vec);
record.set_rdata(RData::DNSSEC(DNSSECRData::NSEC(rdata)));
records.push(record);
}
}
for record in records {
let upserted = self.upsert(record, serial);
debug_assert!(upserted);
}
}
#[cfg(feature = "dnssec")]
fn sign_rrset(
rr_set: &mut RecordSet,
secure_keys: &[Signer],
zone_ttl: u32,
zone_class: DNSClass,
) -> DnsSecResult<()> {
use crate::client::rr::dnssec::tbs;
use crate::client::rr::rdata::SIG;
use chrono::Utc;
let inception = Utc::now();
rr_set.clear_rrsigs();
let rrsig_temp = Record::with(
rr_set.name().clone(),
RecordType::DNSSEC(DNSSECRecordType::RRSIG),
zone_ttl,
);
for signer in secure_keys {
debug!(
"signing rr_set: {}, {} with: {}",
rr_set.name(),
rr_set.record_type(),
signer.algorithm(),
);
let expiration = inception + signer.sig_duration();
let tbs = tbs::rrset_tbs(
rr_set.name(),
zone_class,
rr_set.name().num_labels(),
rr_set.record_type(),
signer.algorithm(),
rr_set.ttl(),
expiration.timestamp() as u32,
inception.timestamp() as u32,
signer.calculate_key_tag()?,
signer.signer_name(),
&rr_set
.records_without_rrsigs()
.cloned()
.collect::<Vec<Record>>(),
);
let tbs = match tbs {
Ok(tbs) => tbs,
Err(err) => {
error!("could not serialize rrset to sign: {}", err);
continue;
}
};
let signature = signer.sign(&tbs);
let signature = match signature {
Ok(signature) => signature,
Err(err) => {
error!("could not sign rrset: {}", err);
continue;
}
};
let mut rrsig = rrsig_temp.clone();
rrsig.set_rdata(RData::DNSSEC(DNSSECRData::SIG(SIG::new(
rr_set.record_type(),
signer.algorithm(),
rr_set.name().num_labels(),
rr_set.ttl(),
expiration.timestamp() as u32,
inception.timestamp() as u32,
signer.calculate_key_tag()?,
signer.signer_name().clone(),
signature,
))));
rr_set.insert_rrsig(rrsig);
}
Ok(())
}
#[cfg(feature = "dnssec")]
fn sign_zone(&mut self) -> DnsSecResult<()> {
use log::warn;
debug!("signing zone: {}", self.origin);
let minimum_ttl = self.minimum_ttl();
let secure_keys = &self.secure_keys;
let records = &mut self.records;
if secure_keys.is_empty() {
warn!("attempt to sign_zone for dnssec, but no keys available!")
}
for rr_set_orig in records.values_mut() {
let rr_set = Arc::make_mut(rr_set_orig);
Self::sign_rrset(rr_set, secure_keys, minimum_ttl, self.class)?;
}
Ok(())
}
}
fn maybe_next_name(
record_set: &RecordSet,
query_type: RecordType,
) -> Option<(LowerName, RecordType)> {
match (record_set.record_type(), query_type) {
(t @ RecordType::ANAME, RecordType::A)
| (t @ RecordType::ANAME, RecordType::AAAA)
| (t @ RecordType::ANAME, RecordType::ANAME) => record_set
.records_without_rrsigs()
.next()
.and_then(|record| record.rdata().as_aname().cloned())
.map(LowerName::from)
.map(|name| (name, t)),
(t @ RecordType::NS, RecordType::NS) => record_set
.records_without_rrsigs()
.next()
.and_then(|record| record.rdata().as_ns().cloned())
.map(LowerName::from)
.map(|name| (name, t)),
(t @ RecordType::CNAME, _) => record_set
.records_without_rrsigs()
.next()
.and_then(|record| record.rdata().as_cname().cloned())
.map(LowerName::from)
.map(|name| (name, t)),
(t @ RecordType::MX, RecordType::MX) => record_set
.records_without_rrsigs()
.next()
.and_then(|record| record.rdata().as_mx())
.map(|mx| mx.exchange().clone())
.map(LowerName::from)
.map(|name| (name, t)),
(t @ RecordType::SRV, RecordType::SRV) => record_set
.records_without_rrsigs()
.next()
.and_then(|record| record.rdata().as_srv())
.map(|srv| srv.target().clone())
.map(LowerName::from)
.map(|name| (name, t)),
_ => None,
}
}
impl Authority for InMemoryAuthority {
type Lookup = AuthLookup;
type LookupFuture = future::Ready<Result<Self::Lookup, LookupError>>;
fn zone_type(&self) -> ZoneType {
self.zone_type
}
fn is_axfr_allowed(&self) -> bool {
self.allow_axfr
}
fn update(&mut self, _update: &MessageRequest) -> UpdateResult<bool> {
Err(ResponseCode::NotImp)
}
fn origin(&self) -> &LowerName {
&self.origin
}
fn lookup(
&self,
name: &LowerName,
query_type: RecordType,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> Pin<Box<dyn Future<Output = Result<Self::Lookup, LookupError>> + Send>> {
let (result, additionals): (LookupResult<LookupRecords>, Option<LookupRecords>) =
match query_type {
RecordType::AXFR | RecordType::ANY => {
let result = AnyRecords::new(
is_secure,
supported_algorithms,
self.records.values().cloned().collect(),
query_type,
name.clone(),
);
(Ok(LookupRecords::AnyRecords(result)), None)
}
_ => {
let answer =
self.inner_lookup(name, query_type, is_secure, supported_algorithms);
let additionals_root_chain_type: Option<(_, _)> = answer
.as_ref()
.and_then(|a| maybe_next_name(&*a, query_type))
.and_then(|(search_name, search_type)| {
self.additional_search(
query_type,
search_name,
search_type,
is_secure,
supported_algorithms,
)
.map(|adds| (adds, search_type))
});
let (additionals, answer) =
match (additionals_root_chain_type, answer, query_type) {
(
Some((additionals, RecordType::ANAME)),
Some(answer),
RecordType::A,
)
| (
Some((additionals, RecordType::ANAME)),
Some(answer),
RecordType::AAAA,
) => {
debug_assert_eq!(answer.record_type(), RecordType::ANAME);
let (rdatas, a_aaaa_ttl) = {
let last_record = additionals.last();
let a_aaaa_ttl =
last_record.map_or(u32::max_value(), |r| r.ttl());
let rdatas: Option<Vec<RData>> = last_record
.and_then(|record| match record.record_type() {
RecordType::A | RecordType::AAAA => {
Some(record.records_without_rrsigs())
}
_ => None,
})
.map(|records| {
records.map(Record::rdata).cloned().collect::<Vec<_>>()
});
(rdatas, a_aaaa_ttl)
};
let ttl = answer.ttl().min(a_aaaa_ttl);
let mut new_answer = RecordSet::new(answer.name(), query_type, ttl);
for rdata in rdatas.into_iter().flatten() {
new_answer.add_rdata(rdata);
}
#[cfg(feature = "dnssec")]
{
use log::warn;
if is_secure {
Self::sign_rrset(
&mut new_answer,
self.secure_keys(),
self.minimum_ttl(),
self.class(),
)
.map_err(|e| warn!("failed to sign ANAME record: {}", e))
.ok();
}
}
let additionals = std::iter::once(answer)
.chain(additionals.into_iter())
.collect();
(Some(additionals), Some(Arc::new(new_answer)))
}
(Some((additionals, _)), answer, _) => (Some(additionals), answer),
(None, answer, _) => (None, answer),
};
let answer = answer
.map_or(Err(LookupError::from(ResponseCode::NXDomain)), |rr_set| {
Ok(LookupRecords::new(is_secure, supported_algorithms, rr_set))
});
let additionals = additionals
.map(|a| LookupRecords::many(is_secure, supported_algorithms, a));
(answer, additionals)
}
};
let result = match result {
Err(LookupError::ResponseCode(ResponseCode::NXDomain)) => {
if self
.records
.keys()
.any(|key| key.name() == name || name.zone_of(key.name()))
{
return Box::pin(future::err(LookupError::NameExists));
} else {
let code = if self.origin().zone_of(name) {
ResponseCode::NXDomain
} else {
ResponseCode::Refused
};
return Box::pin(future::err(LookupError::from(code)));
}
}
Err(e) => return Box::pin(future::err(e)),
o => o,
};
Box::pin(future::ready(
result.map(|answers| AuthLookup::answers(answers, additionals)),
))
}
fn search(
&self,
query: &LowerQuery,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> Pin<Box<dyn Future<Output = Result<Self::Lookup, LookupError>> + Send>> {
debug!("searching InMemoryAuthority for: {}", query);
let lookup_name = query.name();
let record_type: RecordType = query.query_type();
if RecordType::AXFR == record_type {
if !self.is_axfr_allowed() {
return Box::pin(future::err(LookupError::from(ResponseCode::Refused)));
}
#[allow(deprecated)]
match self.zone_type() {
ZoneType::Primary | ZoneType::Secondary | ZoneType::Master | ZoneType::Slave => (),
_ => return Box::pin(future::err(LookupError::from(ResponseCode::NXDomain))),
}
}
match record_type {
RecordType::SOA => {
Box::pin(self.lookup(self.origin(), record_type, is_secure, supported_algorithms))
}
RecordType::AXFR => {
let lookup = future::try_join3(
self.soa_secure(is_secure, supported_algorithms),
self.soa(),
self.lookup(lookup_name, record_type, is_secure, supported_algorithms),
)
.map_ok(|(start_soa, end_soa, records)| match start_soa {
l @ AuthLookup::Empty => l,
start_soa => AuthLookup::AXFR {
start_soa: start_soa.unwrap_records(),
records: records.unwrap_records(),
end_soa: end_soa.unwrap_records(),
},
});
Box::pin(lookup)
}
_ => Box::pin(self.lookup(lookup_name, record_type, is_secure, supported_algorithms)),
}
}
#[cfg(feature = "dnssec")]
fn get_nsec_records(
&self,
name: &LowerName,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> Pin<Box<dyn Future<Output = Result<Self::Lookup, LookupError>> + Send>> {
fn is_nsec_rrset(rr_set: &RecordSet) -> bool {
rr_set.record_type() == RecordType::DNSSEC(DNSSECRecordType::NSEC)
}
let rr_key = RrKey::new(name.clone(), RecordType::DNSSEC(DNSSECRecordType::NSEC));
let no_data = self
.records
.get(&rr_key)
.map(|rr_set| LookupRecords::new(is_secure, supported_algorithms, rr_set.clone()));
if let Some(no_data) = no_data {
return Box::pin(future::ready(Ok(no_data.into())));
}
let get_closest_nsec = |name: &LowerName| -> Option<Arc<RecordSet>> {
self.records
.values()
.rev()
.filter(|rr_set| is_nsec_rrset(rr_set))
.filter(|rr_set| *name >= rr_set.name().into())
.find(|rr_set| {
rr_set
.records(false, SupportedAlgorithms::default())
.next()
.and_then(|r| r.rdata().as_dnssec())
.and_then(DNSSECRData::as_nsec)
.map_or(false, |r| {
*name < r.next_domain_name().into() ||
r.next_domain_name() < rr_set.name()
})
})
.cloned()
};
let closest_proof = get_closest_nsec(name);
let wildcard = name.base_name();
let wildcard = if self.origin().zone_of(&wildcard) {
wildcard
} else {
self.origin().clone()
};
let wildcard_proof = if wildcard != *name {
get_closest_nsec(&wildcard)
} else {
None
};
let proofs = match (closest_proof, wildcard_proof) {
(Some(closest_proof), Some(wildcard_proof)) => {
if wildcard_proof != closest_proof {
vec![wildcard_proof, closest_proof]
} else {
vec![closest_proof]
}
}
(None, Some(proof)) | (Some(proof), None) => vec![proof],
(None, None) => vec![],
};
Box::pin(future::ready(Ok(LookupRecords::many(
is_secure,
supported_algorithms,
proofs,
)
.into())))
}
#[cfg(not(feature = "dnssec"))]
fn get_nsec_records(
&self,
_name: &LowerName,
_is_secure: bool,
_supported_algorithms: SupportedAlgorithms,
) -> Pin<Box<dyn Future<Output = Result<Self::Lookup, LookupError>> + Send>> {
Box::pin(future::ok(AuthLookup::default()))
}
#[cfg(feature = "dnssec")]
fn add_update_auth_key(&mut self, name: Name, key: KEY) -> DnsSecResult<()> {
let rdata = RData::DNSSEC(DNSSECRData::KEY(key));
let record = Record::from_rdata(name, 86400, rdata);
let serial = self.serial();
if self.upsert(record, serial) {
Ok(())
} else {
Err("failed to add auth key".into())
}
}
#[cfg(not(feature = "dnssec"))]
fn add_update_auth_key(&mut self, _name: Name, _key: KEY) -> DnsSecResult<()> {
Err("DNSSEC was not enabled during compilation.".into())
}
#[cfg(feature = "dnssec")]
fn add_zone_signing_key(&mut self, signer: Signer) -> DnsSecResult<()> {
let zone_ttl = self.minimum_ttl();
let dnskey = signer.key().to_dnskey(signer.algorithm())?;
let dnskey = Record::from_rdata(
self.origin.clone().into(),
zone_ttl,
RData::DNSSEC(DNSSECRData::DNSKEY(dnskey)),
);
let serial = self.serial();
self.upsert(dnskey, serial);
self.secure_keys.push(signer);
Ok(())
}
#[cfg(not(feature = "dnssec"))]
fn add_zone_signing_key(&mut self, _signer: Signer) -> DnsSecResult<()> {
Err("DNSSEC was not enabled during compilation.".into())
}
#[cfg(feature = "dnssec")]
fn secure_zone(&mut self) -> DnsSecResult<()> {
self.nsec_zone();
self.increment_soa_serial();
self.sign_zone()
}
#[cfg(not(feature = "dnssec"))]
fn secure_zone(&mut self) -> DnsSecResult<()> {
Err("DNSSEC was not enabled during compilation.".into())
}
}