trust_dns_server/authority/
auth_lookup.rs

1// Copyright 2015-2017 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
8use std::iter::Chain;
9use std::slice::Iter;
10use std::sync::Arc;
11
12use cfg_if::cfg_if;
13
14use crate::authority::{LookupObject, LookupOptions};
15use crate::proto::rr::{LowerName, Record, RecordSet, RecordType, RrsetRecords};
16
17/// The result of a lookup on an Authority
18///
19/// # Lifetimes
20///
21/// * `'c` - the catalogue lifetime
22/// * `'r` - the recordset lifetime, subset of 'c
23/// * `'q` - the queries lifetime
24#[derive(Debug)]
25#[allow(clippy::large_enum_variant)]
26pub enum AuthLookup {
27    /// No records
28    Empty,
29    // TODO: change the result of a lookup to a set of chained iterators...
30    /// Records
31    Records {
32        /// Authoritative answers
33        answers: LookupRecords,
34        /// Optional set of LookupRecords
35        additionals: Option<LookupRecords>,
36    },
37    /// Soa only differs from Records in that the lifetime on the name is from the authority, and not the query
38    SOA(LookupRecords),
39    /// An axfr starts with soa, chained to all the records, then another soa...
40    AXFR {
41        /// The first SOA record in an AXFR response
42        start_soa: LookupRecords,
43        /// The records to return
44        records: LookupRecords,
45        /// The last SOA record of an AXFR (matches the first)
46        end_soa: LookupRecords,
47    },
48}
49
50impl AuthLookup {
51    /// Construct an answer with additional section
52    pub fn answers(answers: LookupRecords, additionals: Option<LookupRecords>) -> Self {
53        Self::Records {
54            answers,
55            additionals,
56        }
57    }
58
59    /// Returns true if either the associated Records are empty, or this is a NameExists or NxDomain
60    pub fn is_empty(&self) -> bool {
61        // TODO: this needs to be cheap
62        self.was_empty()
63    }
64
65    /// This is an NxDomain or NameExists, and has no associated records
66    ///
67    /// this consumes the iterator, and verifies it is empty
68    pub fn was_empty(&self) -> bool {
69        self.iter().count() == 0
70    }
71
72    /// Conversion to an iterator
73    pub fn iter(&self) -> AuthLookupIter<'_> {
74        self.into_iter()
75    }
76
77    /// Does not panic, but will return no records if it is not of that type
78    pub fn unwrap_records(self) -> LookupRecords {
79        match self {
80            // TODO: this is ugly, what about the additionals?
81            Self::Records { answers, .. } => answers,
82            _ => LookupRecords::default(),
83        }
84    }
85
86    /// Takes the additional records, leaving behind None
87    pub fn take_additionals(&mut self) -> Option<LookupRecords> {
88        match self {
89            Self::Records {
90                ref mut additionals,
91                ..
92            } => additionals.take(),
93            _ => None,
94        }
95    }
96}
97
98impl LookupObject for AuthLookup {
99    fn is_empty(&self) -> bool {
100        Self::is_empty(self)
101    }
102
103    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Record> + Send + 'a> {
104        let boxed_iter = Self::iter(self);
105        Box::new(boxed_iter)
106    }
107
108    fn take_additionals(&mut self) -> Option<Box<dyn LookupObject>> {
109        let additionals = Self::take_additionals(self);
110        additionals.map(|a| Box::new(a) as Box<dyn LookupObject>)
111    }
112}
113
114impl Default for AuthLookup {
115    fn default() -> Self {
116        Self::Empty
117    }
118}
119
120impl<'a> IntoIterator for &'a AuthLookup {
121    type Item = &'a Record;
122    type IntoIter = AuthLookupIter<'a>;
123
124    fn into_iter(self) -> Self::IntoIter {
125        match self {
126            AuthLookup::Empty => AuthLookupIter::Empty,
127            // TODO: what about the additionals? is IntoIterator a bad idea?
128            AuthLookup::Records { answers: r, .. } | AuthLookup::SOA(r) => {
129                AuthLookupIter::Records(r.into_iter())
130            }
131            AuthLookup::AXFR {
132                start_soa,
133                records,
134                end_soa,
135            } => AuthLookupIter::AXFR(start_soa.into_iter().chain(records).chain(end_soa)),
136        }
137    }
138}
139
140/// An iterator over an Authority Lookup
141#[allow(clippy::large_enum_variant)]
142#[derive(Default)]
143pub enum AuthLookupIter<'r> {
144    /// The empty set
145    #[default]
146    Empty,
147    /// An iteration over a set of Records
148    Records(LookupRecordsIter<'r>),
149    /// An iteration over an AXFR
150    AXFR(Chain<Chain<LookupRecordsIter<'r>, LookupRecordsIter<'r>>, LookupRecordsIter<'r>>),
151}
152
153impl<'r> Iterator for AuthLookupIter<'r> {
154    type Item = &'r Record;
155
156    fn next(&mut self) -> Option<Self::Item> {
157        match self {
158            AuthLookupIter::Empty => None,
159            AuthLookupIter::Records(i) => i.next(),
160            AuthLookupIter::AXFR(i) => i.next(),
161        }
162    }
163}
164
165impl From<LookupRecords> for AuthLookup {
166    fn from(lookup: LookupRecords) -> Self {
167        Self::Records {
168            answers: lookup,
169            additionals: None,
170        }
171    }
172}
173
174/// An iterator over an ANY query for Records.
175///
176/// The length of this result cannot be known without consuming the iterator.
177///
178/// # Lifetimes
179///
180/// * `'r` - the record_set's lifetime, from the catalog
181/// * `'q` - the lifetime of the query/request
182#[derive(Debug)]
183pub struct AnyRecords {
184    lookup_options: LookupOptions,
185    rrsets: Vec<Arc<RecordSet>>,
186    query_type: RecordType,
187    query_name: LowerName,
188}
189
190impl AnyRecords {
191    /// construct a new lookup of any set of records
192    pub fn new(
193        lookup_options: LookupOptions,
194        // TODO: potentially very expensive
195        rrsets: Vec<Arc<RecordSet>>,
196        query_type: RecordType,
197        query_name: LowerName,
198    ) -> Self {
199        Self {
200            lookup_options,
201            rrsets,
202            query_type,
203            query_name,
204        }
205    }
206
207    fn iter(&self) -> AnyRecordsIter<'_> {
208        self.into_iter()
209    }
210}
211
212impl<'r> IntoIterator for &'r AnyRecords {
213    type Item = &'r Record;
214    type IntoIter = AnyRecordsIter<'r>;
215
216    fn into_iter(self) -> Self::IntoIter {
217        AnyRecordsIter {
218            lookup_options: self.lookup_options,
219            // TODO: potentially very expensive
220            rrsets: self.rrsets.iter(),
221            rrset: None,
222            records: None,
223            query_type: self.query_type,
224            query_name: &self.query_name,
225        }
226    }
227}
228
229/// An iteration over a lookup for any Records
230#[allow(unused)]
231pub struct AnyRecordsIter<'r> {
232    lookup_options: LookupOptions,
233    rrsets: Iter<'r, Arc<RecordSet>>,
234    rrset: Option<&'r RecordSet>,
235    records: Option<RrsetRecords<'r>>,
236    query_type: RecordType,
237    query_name: &'r LowerName,
238}
239
240impl<'r> Iterator for AnyRecordsIter<'r> {
241    type Item = &'r Record;
242
243    fn next(&mut self) -> Option<Self::Item> {
244        use std::borrow::Borrow;
245
246        let query_type = self.query_type;
247        let query_name = self.query_name;
248
249        loop {
250            if let Some(ref mut records) = self.records {
251                let record = records
252                    .by_ref()
253                    .filter(|rr_set| {
254                        query_type == RecordType::ANY || rr_set.record_type() != RecordType::SOA
255                    })
256                    .find(|rr_set| {
257                        query_type == RecordType::AXFR
258                            || &LowerName::from(rr_set.name()) == query_name
259                    });
260
261                if record.is_some() {
262                    return record;
263                }
264            }
265
266            self.rrset = self.rrsets.next().map(Borrow::borrow);
267
268            // if there are no more RecordSets, then return
269            self.rrset?;
270
271            // getting here, we must have exhausted our records from the rrset
272            cfg_if! {
273                if #[cfg(feature = "dnssec")] {
274                    self.records = Some(
275                        self.rrset
276                            .expect("rrset should not be None at this point")
277                            .records(self.lookup_options.is_dnssec(), self.lookup_options.supported_algorithms()),
278                    );
279                } else {
280                    self.records = Some(self.rrset.expect("rrset should not be None at this point").records_without_rrsigs());
281                }
282            }
283        }
284    }
285}
286
287/// The result of a lookup
288#[derive(Debug)]
289pub enum LookupRecords {
290    /// The empty set of records
291    Empty,
292    /// The associate records
293    Records {
294        /// LookupOptions for the request, e.g. dnssec and supported algorithms
295        lookup_options: LookupOptions,
296        /// the records found based on the query
297        records: Arc<RecordSet>,
298    },
299    /// Vec of disjoint record sets
300    ManyRecords(LookupOptions, Vec<Arc<RecordSet>>),
301    // TODO: need a better option for very large zone xfrs...
302    /// A generic lookup response where anything is desired
303    AnyRecords(AnyRecords),
304}
305
306impl LookupRecords {
307    /// Construct a new LookupRecords
308    pub fn new(lookup_options: LookupOptions, records: Arc<RecordSet>) -> Self {
309        Self::Records {
310            lookup_options,
311            records,
312        }
313    }
314
315    /// Construct a new LookupRecords over a set of RecordSets
316    pub fn many(lookup_options: LookupOptions, mut records: Vec<Arc<RecordSet>>) -> Self {
317        // we're reversing the records because they are output in reverse order, via pop()
318        records.reverse();
319        Self::ManyRecords(lookup_options, records)
320    }
321
322    /// This is an NxDomain or NameExists, and has no associated records
323    ///
324    /// this consumes the iterator, and verifies it is empty
325    pub fn was_empty(&self) -> bool {
326        self.iter().count() == 0
327    }
328
329    /// Conversion to an iterator
330    pub fn iter(&self) -> LookupRecordsIter<'_> {
331        self.into_iter()
332    }
333}
334
335impl Default for LookupRecords {
336    fn default() -> Self {
337        Self::Empty
338    }
339}
340
341impl<'a> IntoIterator for &'a LookupRecords {
342    type Item = &'a Record;
343    type IntoIter = LookupRecordsIter<'a>;
344
345    #[allow(unused_variables)]
346    fn into_iter(self) -> Self::IntoIter {
347        match self {
348            LookupRecords::Empty => LookupRecordsIter::Empty,
349            LookupRecords::Records {
350                lookup_options,
351                records,
352            } => LookupRecordsIter::RecordsIter(
353                lookup_options.rrset_with_supported_algorithms(records),
354            ),
355            LookupRecords::ManyRecords(lookup_options, r) => LookupRecordsIter::ManyRecordsIter(
356                r.iter()
357                    .map(|r| lookup_options.rrset_with_supported_algorithms(r))
358                    .collect(),
359                None,
360            ),
361            LookupRecords::AnyRecords(r) => LookupRecordsIter::AnyRecordsIter(r.iter()),
362        }
363    }
364}
365
366/// Iterator over lookup records
367#[derive(Default)]
368pub enum LookupRecordsIter<'r> {
369    /// An iteration over batch record type results
370    AnyRecordsIter(AnyRecordsIter<'r>),
371    /// An iteration over a single RecordSet
372    RecordsIter(RrsetRecords<'r>),
373    /// An iteration over many rrsets
374    ManyRecordsIter(Vec<RrsetRecords<'r>>, Option<RrsetRecords<'r>>),
375    /// An empty set
376    #[default]
377    Empty,
378}
379
380impl<'r> Iterator for LookupRecordsIter<'r> {
381    type Item = &'r Record;
382
383    fn next(&mut self) -> Option<Self::Item> {
384        match self {
385            LookupRecordsIter::Empty => None,
386            LookupRecordsIter::AnyRecordsIter(current) => current.next(),
387            LookupRecordsIter::RecordsIter(current) => current.next(),
388            LookupRecordsIter::ManyRecordsIter(set, ref mut current) => loop {
389                if let Some(o) = current.as_mut().and_then(Iterator::next) {
390                    return Some(o);
391                }
392
393                *current = set.pop();
394                if current.is_none() {
395                    return None;
396                }
397            },
398        }
399    }
400}
401
402impl From<AnyRecords> for LookupRecords {
403    fn from(rrset_records: AnyRecords) -> Self {
404        Self::AnyRecords(rrset_records)
405    }
406}
407
408impl LookupObject for LookupRecords {
409    fn is_empty(&self) -> bool {
410        Self::was_empty(self)
411    }
412
413    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Record> + Send + 'a> {
414        Box::new(self.iter())
415    }
416
417    fn take_additionals(&mut self) -> Option<Box<dyn LookupObject>> {
418        None
419    }
420}