trust_dns_server/authority/
authority.rs

1// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! All authority related types
9
10use cfg_if::cfg_if;
11
12#[cfg(feature = "dnssec")]
13use crate::proto::rr::{
14    dnssec::{rdata::key::KEY, DnsSecResult, SigSigner, SupportedAlgorithms},
15    Name,
16};
17use crate::{
18    authority::{LookupError, MessageRequest, UpdateResult, ZoneType},
19    proto::rr::{LowerName, RecordSet, RecordType, RrsetRecords},
20    server::RequestInfo,
21};
22
23/// LookupOptions that specify different options from the client to include or exclude various records in the response.
24///
25/// For example, `is_dnssec` will include `RRSIG` in the response, `supported_algorithms` will only include a subset of
26///    `RRSIG` based on the algorithms supported by the request.
27#[derive(Clone, Copy, Debug, Default)]
28pub struct LookupOptions {
29    is_dnssec: bool,
30    #[cfg(feature = "dnssec")]
31    supported_algorithms: SupportedAlgorithms,
32}
33
34/// Lookup Options for the request to the authority
35impl LookupOptions {
36    /// Return a new LookupOptions
37    #[cfg(feature = "dnssec")]
38    #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
39    pub fn for_dnssec(is_dnssec: bool, supported_algorithms: SupportedAlgorithms) -> Self {
40        Self {
41            is_dnssec,
42            supported_algorithms,
43        }
44    }
45
46    /// Specify that this lookup should return DNSSEC related records as well, e.g. RRSIG
47    #[allow(clippy::needless_update)]
48    pub fn set_is_dnssec(self, val: bool) -> Self {
49        Self {
50            is_dnssec: val,
51            ..self
52        }
53    }
54
55    /// If true this lookup should return DNSSEC related records as well, e.g. RRSIG
56    pub fn is_dnssec(&self) -> bool {
57        self.is_dnssec
58    }
59
60    /// Specify the algorithms for which DNSSEC records should be returned
61    #[cfg(feature = "dnssec")]
62    #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
63    pub fn set_supported_algorithms(self, val: SupportedAlgorithms) -> Self {
64        Self {
65            supported_algorithms: val,
66            ..self
67        }
68    }
69
70    /// The algorithms for which DNSSEC records should be returned
71    #[cfg(feature = "dnssec")]
72    #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
73    pub fn supported_algorithms(&self) -> SupportedAlgorithms {
74        self.supported_algorithms
75    }
76
77    /// Returns the subset of the rrset limited to the supported_algorithms
78    pub fn rrset_with_supported_algorithms<'r>(
79        &self,
80        record_set: &'r RecordSet,
81    ) -> RrsetRecords<'r> {
82        cfg_if! {
83            if #[cfg(feature = "dnssec")] {
84                record_set.records(
85                    self.is_dnssec(),
86                    self.supported_algorithms(),
87                )
88            } else {
89                record_set.records_without_rrsigs()
90            }
91        }
92    }
93}
94
95/// Authority implementations can be used with a `Catalog`
96#[async_trait::async_trait]
97pub trait Authority: Send + Sync {
98    /// Result of a lookup
99    type Lookup: Send + Sync + Sized + 'static;
100
101    /// What type is this zone
102    fn zone_type(&self) -> ZoneType;
103
104    /// Return true if AXFR is allowed
105    fn is_axfr_allowed(&self) -> bool;
106
107    /// Perform a dynamic update of a zone
108    async fn update(&self, update: &MessageRequest) -> UpdateResult<bool>;
109
110    /// Get the origin of this zone, i.e. example.com is the origin for www.example.com
111    fn origin(&self) -> &LowerName;
112
113    /// Looks up all Resource Records matching the giving `Name` and `RecordType`.
114    ///
115    /// # Arguments
116    ///
117    /// * `name` - The `Name`, label, to lookup.
118    /// * `rtype` - The `RecordType`, to lookup. `RecordType::ANY` will return all records matching
119    ///             `name`. `RecordType::AXFR` will return all record types except `RecordType::SOA`
120    ///             due to the requirements that on zone transfers the `RecordType::SOA` must both
121    ///             precede and follow all other records.
122    /// * `is_secure` - If the DO bit is set on the EDNS OPT record, then return RRSIGs as well.
123    ///
124    /// # Return value
125    ///
126    /// None if there are no matching records, otherwise a `Vec` containing the found records.
127    async fn lookup(
128        &self,
129        name: &LowerName,
130        rtype: RecordType,
131        lookup_options: LookupOptions,
132    ) -> Result<Self::Lookup, LookupError>;
133
134    /// Using the specified query, perform a lookup against this zone.
135    ///
136    /// # Arguments
137    ///
138    /// * `query` - the query to perform the lookup with.
139    /// * `is_secure` - if true, then RRSIG records (if this is a secure zone) will be returned.
140    ///
141    /// # Return value
142    ///
143    /// Returns a vector containing the results of the query, it will be empty if not found. If
144    ///  `is_secure` is true, in the case of no records found then NSEC records will be returned.
145    async fn search(
146        &self,
147        request: RequestInfo<'_>,
148        lookup_options: LookupOptions,
149    ) -> Result<Self::Lookup, LookupError>;
150
151    /// Get the NS, NameServer, record for the zone
152    async fn ns(&self, lookup_options: LookupOptions) -> Result<Self::Lookup, LookupError> {
153        self.lookup(self.origin(), RecordType::NS, lookup_options)
154            .await
155    }
156
157    /// Return the NSEC records based on the given name
158    ///
159    /// # Arguments
160    ///
161    /// * `name` - given this name (i.e. the lookup name), return the NSEC record that is less than
162    ///            this
163    /// * `is_secure` - if true then it will return RRSIG records as well
164    async fn get_nsec_records(
165        &self,
166        name: &LowerName,
167        lookup_options: LookupOptions,
168    ) -> Result<Self::Lookup, LookupError>;
169
170    /// Returns the SOA of the authority.
171    ///
172    /// *Note*: This will only return the SOA, if this is fulfilling a request, a standard lookup
173    ///  should be used, see `soa_secure()`, which will optionally return RRSIGs.
174    async fn soa(&self) -> Result<Self::Lookup, LookupError> {
175        // SOA should be origin|SOA
176        self.lookup(self.origin(), RecordType::SOA, LookupOptions::default())
177            .await
178    }
179
180    /// Returns the SOA record for the zone
181    async fn soa_secure(&self, lookup_options: LookupOptions) -> Result<Self::Lookup, LookupError> {
182        self.lookup(self.origin(), RecordType::SOA, lookup_options)
183            .await
184    }
185}
186
187/// Extension to Authority to allow for DNSSEC features
188#[cfg(feature = "dnssec")]
189#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
190#[async_trait::async_trait]
191pub trait DnssecAuthority: Authority {
192    /// Add a (Sig0) key that is authorized to perform updates against this authority
193    async fn add_update_auth_key(&self, name: Name, key: KEY) -> DnsSecResult<()>;
194
195    /// Add Signer
196    async fn add_zone_signing_key(&self, signer: SigSigner) -> DnsSecResult<()>;
197
198    /// Sign the zone for DNSSEC
199    async fn secure_zone(&self) -> DnsSecResult<()>;
200}