hickory_server/authority/
mod.rs

1// Copyright 2015-2019 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! Module for `Catalog` of `Authority` zones which are responsible for storing `RRSet` records.
9
10use std::{io, sync::Arc};
11
12use enum_as_inner::EnumAsInner;
13use thiserror::Error;
14
15use crate::proto::op::ResponseCode;
16use crate::proto::rr::{Record, rdata::SOA};
17use crate::proto::{ProtoError, ProtoErrorKind};
18#[cfg(feature = "recursor")]
19use crate::recursor::ErrorKind;
20#[cfg(feature = "resolver")]
21use crate::resolver::ResolveError;
22
23mod auth_lookup;
24#[allow(clippy::module_inception)]
25mod authority;
26pub(crate) mod authority_object;
27mod catalog;
28pub(crate) mod message_request;
29mod message_response;
30
31pub use self::auth_lookup::{
32    AnyRecords, AuthLookup, AuthLookupIter, LookupRecords, LookupRecordsIter,
33};
34pub use self::authority::{Authority, LookupControlFlow, LookupOptions};
35#[cfg(feature = "__dnssec")]
36pub use self::authority::{DnssecAuthority, Nsec3QueryInfo};
37pub use self::authority_object::{AuthorityObject, DnssecSummary, EmptyLookup, LookupObject};
38pub use self::catalog::Catalog;
39pub use self::message_request::{MessageRequest, Queries, UpdateRequest};
40pub use self::message_response::{MessageResponse, MessageResponseBuilder};
41
42/// Result of an Update operation
43pub type UpdateResult<T> = Result<T, ResponseCode>;
44
45// TODO: should this implement Failure?
46#[allow(clippy::large_enum_variant)]
47/// A query could not be fulfilled
48#[derive(Debug, EnumAsInner, Error)]
49#[non_exhaustive]
50pub enum LookupError {
51    /// A record at the same Name as the query exists, but not of the queried RecordType
52    #[error("The name exists, but not for the record requested")]
53    NameExists,
54    /// There was an error performing the lookup
55    #[error("Error performing lookup: {0}")]
56    ResponseCode(ResponseCode),
57    /// Proto error
58    #[error("Proto error: {0}")]
59    ProtoError(#[from] ProtoError),
60    /// Resolve Error
61    #[cfg(feature = "resolver")]
62    #[error("Forward resolution error: {0}")]
63    ResolveError(#[from] ResolveError),
64    /// Recursive Resolver Error
65    #[cfg(feature = "recursor")]
66    #[error("Recursive resolution error: {0}")]
67    RecursiveError(#[from] hickory_recursor::Error),
68    /// An underlying IO error occurred
69    #[error("io error: {0}")]
70    Io(io::Error),
71}
72
73impl LookupError {
74    /// Create a lookup error, specifying that a name exists at the location, but no matching RecordType
75    pub fn for_name_exists() -> Self {
76        Self::NameExists
77    }
78
79    /// This is a non-existent domain name
80    pub fn is_nx_domain(&self) -> bool {
81        match self {
82            Self::ResponseCode(ResponseCode::NXDomain) => true,
83            #[cfg(feature = "resolver")]
84            Self::ResolveError(e) if e.is_nx_domain() => true,
85            #[cfg(feature = "recursor")]
86            Self::RecursiveError(e) if e.is_nx_domain() => true,
87            _ => false,
88        }
89    }
90
91    /// Returns true if no records were returned
92    pub fn is_no_records_found(&self) -> bool {
93        match self {
94            #[cfg(feature = "resolver")]
95            Self::ResolveError(e) if e.is_no_records_found() => true,
96            #[cfg(feature = "recursor")]
97            Self::RecursiveError(e) if e.is_no_records_found() => true,
98            _ => false,
99        }
100    }
101
102    /// Returns the SOA record, if the error contains one
103    pub fn into_soa(self) -> Option<Box<Record<SOA>>> {
104        match self {
105            #[cfg(feature = "resolver")]
106            Self::ResolveError(e) => e.into_soa(),
107            #[cfg(feature = "recursor")]
108            Self::RecursiveError(e) => e.into_soa(),
109            _ => None,
110        }
111    }
112
113    /// Return authority records
114    pub fn authorities(&self) -> Option<Arc<[Record]>> {
115        match self {
116            Self::ProtoError(e) => match e.kind() {
117                ProtoErrorKind::NoRecordsFound { authorities, .. } => authorities.clone(),
118                _ => None,
119            },
120            #[cfg(feature = "recursor")]
121            Self::RecursiveError(e) => match e.kind() {
122                ErrorKind::Forward(fwd) => fwd.authorities.clone(),
123                ErrorKind::Proto(proto) => match proto.kind() {
124                    ProtoErrorKind::NoRecordsFound { authorities, .. } => authorities.clone(),
125                    _ => None,
126                },
127                _ => None,
128            },
129            _ => None,
130        }
131    }
132
133    /// This is a non-existent domain name
134    pub fn is_refused(&self) -> bool {
135        matches!(*self, Self::ResponseCode(ResponseCode::Refused))
136    }
137}
138
139impl From<ResponseCode> for LookupError {
140    fn from(code: ResponseCode) -> Self {
141        // this should never be a NoError
142        debug_assert!(code != ResponseCode::NoError);
143        Self::ResponseCode(code)
144    }
145}
146
147impl From<io::Error> for LookupError {
148    fn from(e: io::Error) -> Self {
149        Self::Io(e)
150    }
151}
152
153impl From<LookupError> for io::Error {
154    fn from(e: LookupError) -> Self {
155        Self::new(io::ErrorKind::Other, Box::new(e))
156    }
157}
158
159#[allow(deprecated)]
160mod zone_type {
161    use serde::{Deserialize, Serialize};
162
163    /// The type of zone stored in a Catalog
164    #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy)]
165    pub enum ZoneType {
166        /// This authority for a zone
167        Primary,
168        /// This authority for a zone, i.e. the Primary
169        #[deprecated = "please read about Juneteenth"]
170        Master,
171        /// A secondary, i.e. replicated from the Primary
172        Secondary,
173        /// A secondary, i.e. replicated from the Primary
174        #[deprecated = "please read about Juneteenth"]
175        Slave,
176        /// A cached zone that queries other nameservers
177        External,
178    }
179
180    impl ZoneType {
181        /// Is this an authoritative Authority, i.e. it owns the records of the zone.
182        #[allow(deprecated)]
183        pub fn is_authoritative(self) -> bool {
184            matches!(
185                self,
186                Self::Primary | Self::Secondary | Self::Master | Self::Slave
187            )
188        }
189    }
190}
191
192pub use zone_type::ZoneType;