Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trivariant LookupResult type to allow authorities to decline to respond to a query #2160

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
112 changes: 106 additions & 6 deletions crates/server/src/authority/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ pub trait Authority: Send + Sync {
name: &LowerName,
rtype: RecordType,
lookup_options: LookupOptions,
) -> Result<Self::Lookup, LookupError>;
) -> LookupResult<Self::Lookup>;

/// Using the specified query, perform a lookup against this zone.
///
Expand All @@ -146,10 +146,10 @@ pub trait Authority: Send + Sync {
&self,
request: RequestInfo<'_>,
lookup_options: LookupOptions,
) -> Result<Self::Lookup, LookupError>;
) -> LookupResult<Self::Lookup>;

/// Get the NS, NameServer, record for the zone
async fn ns(&self, lookup_options: LookupOptions) -> Result<Self::Lookup, LookupError> {
async fn ns(&self, lookup_options: LookupOptions) -> LookupResult<Self::Lookup> {
self.lookup(self.origin(), RecordType::NS, lookup_options)
.await
}
Expand All @@ -165,20 +165,20 @@ pub trait Authority: Send + Sync {
&self,
name: &LowerName,
lookup_options: LookupOptions,
) -> Result<Self::Lookup, LookupError>;
) -> LookupResult<Self::Lookup>;

/// Returns the SOA of the authority.
///
/// *Note*: This will only return the SOA, if this is fulfilling a request, a standard lookup
/// should be used, see `soa_secure()`, which will optionally return RRSIGs.
async fn soa(&self) -> Result<Self::Lookup, LookupError> {
async fn soa(&self) -> LookupResult<Self::Lookup> {
// SOA should be origin|SOA
self.lookup(self.origin(), RecordType::SOA, LookupOptions::default())
.await
}

/// Returns the SOA record for the zone
async fn soa_secure(&self, lookup_options: LookupOptions) -> Result<Self::Lookup, LookupError> {
async fn soa_secure(&self, lookup_options: LookupOptions) -> LookupResult<Self::Lookup> {
self.lookup(self.origin(), RecordType::SOA, lookup_options)
.await
}
Expand All @@ -198,3 +198,103 @@ pub trait DnssecAuthority: Authority {
/// Sign the zone for DNSSEC
async fn secure_zone(&self) -> DnsSecResult<()>;
}

/// Result of a Lookup in the Catalog and Authority
pub enum LookupResult<T, E = LookupError> {
djc marked this conversation as resolved.
Show resolved Hide resolved
/// A successful lookup result.
Ok(T),
/// An error was encountered during the lookup.
Err(E),
/// The authority did not answer the query and the next authority in the chain should
/// be consulted. Do not use this for any other purpose, in particular, do not use it
/// to represent an empty lookup (use Ok(EmptyLookup) for that.)
Bypass,
djc marked this conversation as resolved.
Show resolved Hide resolved
}

/// The following are a minimal set of methods typically used with Result or Option, and that
/// were used in the server code or test suite prior to when the LookupResult type was created
/// (authority lookup functions previously returned a Result over a Lookup or LookupError type.)
impl<T, E: std::fmt::Display> LookupResult<T, E> {
/// Return LookupResult::Ok variant or panic with a custom error message.
pub fn expect(self, msg: &str) -> T {
match self {
Self::Ok(lookup) => lookup,
Self::Err(_e) => {
panic!("LookupResult expect called on LookupResult::Err: {msg}");
}
Self::Bypass => {
panic!("LookupResult expect called on LookupResult::Bypass: {msg}");
}
}
}

/// Return LookupResult::Err variant or panic with a custom error message.
pub fn expect_err(self, msg: &str) -> E {
match self {
Self::Ok(_) => {
panic!("LookupResult::expect_err called on LookupResult::Ok value: {msg}");
}
Self::Err(e) => e,
Self::Bypass => {
panic!("LookupResult::expect_err called on LookupResult::Bypass value: {msg}");
}
}
}

/// Return LookupResult::Ok variant or panic
pub fn unwrap(self) -> T {
match self {
Self::Ok(lookup) => lookup,
Self::Err(e) => {
panic!("LookupResult unwrap called on LookupResult::Err: {e}");
}
Self::Bypass => {
panic!("LookupResult unwrap called on LookupResult::Bypass");
}
}
}

/// Return LookupResult::Err variant or panic
pub fn unwrap_err(self) -> E {
match self {
Self::Ok(_) => {
panic!("LookupResult::unwrap_err called on LookupResult::Ok value");
}
Self::Err(e) => e,
Self::Bypass => {
panic!("LookupResult::unwrap_err called on LookupResult::Bypass");
}
}
}

/// Return Ok Variant or default value
pub fn unwrap_or_default(self) -> T
where
T: Default,
{
match self {
Self::Ok(lookup) => lookup,
_ => T::default(),
}
}

/// Maps LookupResult::Ok(T) to LookupResult::Ok(U), passing LookupResult::Err and
/// LookupResult::Bypass values unchanged.
pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> LookupResult<U, E> {
match self {
Self::Ok(t) => LookupResult::<U, E>::Ok(op(t)),
Self::Err(e) => LookupResult::<U, E>::Err(e),
Self::Bypass => LookupResult::<U, E>::Bypass,
}
}

/// Maps LookupResult::Err(T) to LookupResult::Err(U), passing LookupResult::Ok and
/// LookupResult::Bypass values unchanged.
pub fn map_err<U, F: FnOnce(E) -> U>(self, op: F) -> LookupResult<T, U> {
match self {
Self::Ok(t) => LookupResult::<T, U>::Ok(t),
Self::Err(e) => LookupResult::<T, U>::Err(op(e)),
Self::Bypass => LookupResult::<T, U>::Bypass,
}
}
}
23 changes: 10 additions & 13 deletions crates/server/src/authority/authority_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::sync::Arc;
use tracing::debug;

use crate::{
authority::{Authority, LookupError, LookupOptions, MessageRequest, UpdateResult, ZoneType},
authority::{Authority, LookupOptions, LookupResult, MessageRequest, UpdateResult, ZoneType},
proto::rr::{LowerName, Record, RecordType},
server::RequestInfo,
};
Expand Down Expand Up @@ -54,7 +54,7 @@ pub trait AuthorityObject: Send + Sync {
name: &LowerName,
rtype: RecordType,
lookup_options: LookupOptions,
) -> Result<Box<dyn LookupObject>, LookupError>;
) -> LookupResult<Box<dyn LookupObject>>;

/// Using the specified query, perform a lookup against this zone.
///
Expand All @@ -71,13 +71,10 @@ pub trait AuthorityObject: Send + Sync {
&self,
request_info: RequestInfo<'_>,
lookup_options: LookupOptions,
) -> Result<Box<dyn LookupObject>, LookupError>;
) -> LookupResult<Box<dyn LookupObject>>;

/// Get the NS, NameServer, record for the zone
async fn ns(
&self,
lookup_options: LookupOptions,
) -> Result<Box<dyn LookupObject>, LookupError> {
async fn ns(&self, lookup_options: LookupOptions) -> LookupResult<Box<dyn LookupObject>> {
self.lookup(self.origin(), RecordType::NS, lookup_options)
.await
}
Expand All @@ -93,13 +90,13 @@ pub trait AuthorityObject: Send + Sync {
&self,
name: &LowerName,
lookup_options: LookupOptions,
) -> Result<Box<dyn LookupObject>, LookupError>;
) -> LookupResult<Box<dyn LookupObject>>;

/// Returns the SOA of the authority.
///
/// *Note*: This will only return the SOA, if this is fulfilling a request, a standard lookup
/// should be used, see `soa_secure()`, which will optionally return RRSIGs.
async fn soa(&self) -> Result<Box<dyn LookupObject>, LookupError> {
async fn soa(&self) -> LookupResult<Box<dyn LookupObject>> {
// SOA should be origin|SOA
self.lookup(self.origin(), RecordType::SOA, LookupOptions::default())
.await
Expand All @@ -109,7 +106,7 @@ pub trait AuthorityObject: Send + Sync {
async fn soa_secure(
&self,
lookup_options: LookupOptions,
) -> Result<Box<dyn LookupObject>, LookupError> {
) -> LookupResult<Box<dyn LookupObject>> {
self.lookup(self.origin(), RecordType::SOA, lookup_options)
.await
}
Expand Down Expand Up @@ -164,7 +161,7 @@ where
name: &LowerName,
rtype: RecordType,
lookup_options: LookupOptions,
) -> Result<Box<dyn LookupObject>, LookupError> {
) -> LookupResult<Box<dyn LookupObject>> {
let this = self.as_ref();
let lookup = Authority::lookup(this, name, rtype, lookup_options).await;
lookup.map(|l| Box::new(l) as Box<dyn LookupObject>)
djc marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -185,7 +182,7 @@ where
&self,
request_info: RequestInfo<'_>,
lookup_options: LookupOptions,
) -> Result<Box<dyn LookupObject>, LookupError> {
) -> LookupResult<Box<dyn LookupObject>> {
let this = self.as_ref();
debug!("performing {} on {}", request_info.query, this.origin());
let lookup = Authority::search(this, request_info, lookup_options).await;
Expand All @@ -203,7 +200,7 @@ where
&self,
name: &LowerName,
lookup_options: LookupOptions,
) -> Result<Box<dyn LookupObject>, LookupError> {
) -> LookupResult<Box<dyn LookupObject>> {
let lookup = Authority::get_nsec_records(self.as_ref(), name, lookup_options).await;
lookup.map(|l| Box::new(l) as Box<dyn LookupObject>)
}
Expand Down