domain/base/name/
traits.rs

1//! Domain name-related traits.
2//!
3//! This is a private module. Its public traits are re-exported by the parent.
4
5use super::chain::{Chain, LongChainError};
6use super::dname::Dname;
7use super::label::Label;
8use super::relative::RelativeDname;
9#[cfg(feature = "bytes")]
10use bytes::Bytes;
11use core::cmp;
12use core::convert::Infallible;
13use octseq::builder::{
14    infallible, BuilderAppendError, EmptyBuilder, FreezeBuilder, FromBuilder,
15    OctetsBuilder, ShortBuf,
16};
17#[cfg(feature = "std")]
18use std::borrow::Cow;
19
20//------------ ToLabelIter ---------------------------------------------------
21
22/// A type that can produce an iterator over its labels.
23///
24/// This trait is used as a trait bound for both [`ToDname`] and
25/// [`ToRelativeDname`]. It is separate since it has to be generic over the
26/// lifetime of the label reference but we don’t want to have this lifetime
27/// parameter pollute those traits.
28///
29/// [`ToDname`]: trait.ToDname.html
30/// [`ToRelativeDname`]: trait ToRelativeDname.html
31#[allow(clippy::len_without_is_empty)]
32pub trait ToLabelIter {
33    /// The type of the iterator over the labels.
34    ///
35    /// This iterator types needs to be double ended so that we can deal with
36    /// name suffixes. It needs to be cloneable to be able to cascade over
37    /// parents of a name.
38    type LabelIter<'a>: Iterator<Item = &'a Label>
39        + DoubleEndedIterator
40        + Clone
41    where
42        Self: 'a;
43
44    /// Returns an iterator over the labels.
45    fn iter_labels(&self) -> Self::LabelIter<'_>;
46
47    /// Returns the length in octets of the encoded name.
48    fn compose_len(&self) -> u16 {
49        self.iter_labels().map(|label| label.compose_len()).sum()
50    }
51
52    /// Determines whether `base` is a prefix of `self`.
53    fn starts_with<N: ToLabelIter + ?Sized>(&self, base: &N) -> bool {
54        let mut self_iter = self.iter_labels();
55        let mut base_iter = base.iter_labels();
56        loop {
57            match (self_iter.next(), base_iter.next()) {
58                (Some(sl), Some(bl)) => {
59                    if sl != bl {
60                        return false;
61                    }
62                }
63                (_, None) => return true,
64                (None, Some(_)) => return false,
65            }
66        }
67    }
68
69    /// Determines whether `base` is a suffix of `self`.
70    fn ends_with<N: ToLabelIter + ?Sized>(&self, base: &N) -> bool {
71        let mut self_iter = self.iter_labels();
72        let mut base_iter = base.iter_labels();
73        loop {
74            match (self_iter.next_back(), base_iter.next_back()) {
75                (Some(sl), Some(bl)) => {
76                    if sl != bl {
77                        return false;
78                    }
79                }
80                (_, None) => return true,
81                (None, Some(_)) => return false,
82            }
83        }
84    }
85}
86
87impl<'r, N: ToLabelIter + ?Sized> ToLabelIter for &'r N {
88    type LabelIter<'a> = N::LabelIter<'a> where 'r: 'a, N: 'a;
89
90    fn iter_labels(&self) -> Self::LabelIter<'_> {
91        (*self).iter_labels()
92    }
93}
94
95//------------ ToDname -------------------------------------------------------
96
97/// A type that represents an absolute domain name.
98///
99/// An absolute domain name is a sequence of labels where the last label is
100/// the root label and where the wire-format representation is not longer than
101/// 255 characters. Implementers of this trait need to provide access to the
102/// label sequence via an iterator and know how to compose the wire-format
103/// representation into a buffer.
104///
105/// The most common types implementing this trait are [`Dname`],
106/// [`ParsedDname`], and [`Chain<L, R>`] where `R` is `ToDname` itself.
107///
108/// [`Chain<L, R>`]: struct.Chain.html
109/// [`Dname`]: struct.Dname.html
110/// [`ParsedDname`]: struct.ParsedDname.html
111pub trait ToDname: ToLabelIter {
112    /// Converts the name into a single, uncompressed name.
113    ///
114    /// The default implementation provided by the trait iterates over the
115    /// labels of the name and adds them one by one to [`Dname`]. This will
116    /// work for any name but an optimized implementation can be provided for
117    /// some types of names.
118    ///
119    /// [`Dname`]: struct.Dname.html
120    fn to_dname<Octets>(
121        &self,
122    ) -> Result<Dname<Octets>, BuilderAppendError<Octets>>
123    where
124        Octets: FromBuilder,
125        <Octets as FromBuilder>::Builder: EmptyBuilder,
126    {
127        let mut builder =
128            Octets::Builder::with_capacity(self.compose_len().into());
129        self.iter_labels()
130            .try_for_each(|label| label.compose(&mut builder))?;
131        Ok(unsafe { Dname::from_octets_unchecked(builder.freeze()) })
132    }
133
134    /// Converts the name into a single name in canonical form.
135    fn to_canonical_dname<Octets>(
136        &self,
137    ) -> Result<Dname<Octets>, BuilderAppendError<Octets>>
138    where
139        Octets: FromBuilder,
140        <Octets as FromBuilder>::Builder: EmptyBuilder,
141    {
142        let mut builder =
143            Octets::Builder::with_capacity(self.compose_len().into());
144        self.iter_labels()
145            .try_for_each(|label| label.compose_canonical(&mut builder))?;
146        Ok(unsafe { Dname::from_octets_unchecked(builder.freeze()) })
147    }
148
149    /// Returns an octets slice of the content if possible.
150    ///
151    /// If a value stores the domain name as one single octets sequence, it
152    /// should return a reference to this sequence here. If the name is
153    /// composed from multiple such sequences, it should return `None`.
154    ///
155    /// This method is used to optimize comparision operations between
156    /// two values that are indeed flat names.
157    fn as_flat_slice(&self) -> Option<&[u8]> {
158        None
159    }
160
161    fn compose<Target: OctetsBuilder + ?Sized>(
162        &self,
163        target: &mut Target,
164    ) -> Result<(), Target::AppendError> {
165        if let Some(slice) = self.as_flat_slice() {
166            target.append_slice(slice)
167        } else {
168            for label in self.iter_labels() {
169                label.compose(target)?;
170            }
171            Ok(())
172        }
173    }
174
175    fn compose_canonical<Target: OctetsBuilder + ?Sized>(
176        &self,
177        target: &mut Target,
178    ) -> Result<(), Target::AppendError> {
179        for label in self.iter_labels() {
180            label.compose_canonical(target)?;
181        }
182        Ok(())
183    }
184
185    /// Returns a cow of the domain name.
186    ///
187    /// If the name is available as one single slice – i.e.,
188    /// [`as_flat_slice`] returns ‘some,’ creates the borrowed variant from
189    /// that slice. Otherwise assembles an owned variant via [`to_dname`].
190    ///
191    /// [`as_flat_slice`]: #method.as_flat_slice
192    /// [`to_dname`]: #method.to_dname
193    #[cfg(feature = "std")]
194    fn to_cow(&self) -> Dname<std::borrow::Cow<[u8]>> {
195        let octets = self
196            .as_flat_slice()
197            .map(Cow::Borrowed)
198            .unwrap_or_else(|| Cow::Owned(self.to_vec().into_octets()));
199        unsafe { Dname::from_octets_unchecked(octets) }
200    }
201
202    /// Returns the domain name assembled into a `Vec<u8>`.
203    #[cfg(feature = "std")]
204    fn to_vec(&self) -> Dname<std::vec::Vec<u8>> {
205        infallible(self.to_dname())
206    }
207
208    /// Returns the domain name assembled into a bytes value.
209    #[cfg(feature = "bytes")]
210    fn to_bytes(&self) -> Dname<Bytes> {
211        infallible(self.to_dname())
212    }
213
214    /// Tests whether `self` and `other` are equal.
215    ///
216    /// This method can be used to implement `PartialEq` on types implementing
217    /// `ToDname` since a blanket implementation for all pairs of `ToDname`
218    /// is currently impossible.
219    ///
220    /// Domain names are compared ignoring ASCII case.
221    fn name_eq<N: ToDname + ?Sized>(&self, other: &N) -> bool {
222        if let (Some(left), Some(right)) =
223            (self.as_flat_slice(), other.as_flat_slice())
224        {
225            // We can do this because the length octets of each label are in
226            // the ranged 0..64 which is before all ASCII letters.
227            left.eq_ignore_ascii_case(right)
228        } else {
229            self.iter_labels().eq(other.iter_labels())
230        }
231    }
232
233    /// Returns the ordering between `self` and `other`.
234    ///
235    /// This method can be used to implement both `PartialOrd` and `Ord` on
236    /// types implementing `ToDname` since a blanket implementation for all
237    /// pairs of `ToDname`s is currently not possible.
238    ///
239    /// Domain name order is determined according to the ‘canonical DNS
240    /// name order’ as defined in [section 6.1 of RFC 4034][RFC4034-6.1].
241    ///
242    /// [RFC4034-6.1]: https://tools.ietf.org/html/rfc4034#section-6.1
243    fn name_cmp<N: ToDname + ?Sized>(&self, other: &N) -> cmp::Ordering {
244        let mut self_iter = self.iter_labels();
245        let mut other_iter = other.iter_labels();
246        loop {
247            match (self_iter.next_back(), other_iter.next_back()) {
248                (Some(left), Some(right)) => match left.cmp(right) {
249                    cmp::Ordering::Equal => {}
250                    res => return res,
251                },
252                (None, Some(_)) => return cmp::Ordering::Less,
253                (Some(_), None) => return cmp::Ordering::Greater,
254                (None, None) => return cmp::Ordering::Equal,
255            }
256        }
257    }
258
259    /// Returns the composed name ordering.
260    fn composed_cmp<N: ToDname + ?Sized>(&self, other: &N) -> cmp::Ordering {
261        if let (Some(left), Some(right)) =
262            (self.as_flat_slice(), other.as_flat_slice())
263        {
264            return left.cmp(right);
265        }
266        let mut self_iter = self.iter_labels();
267        let mut other_iter = other.iter_labels();
268        loop {
269            match (self_iter.next(), other_iter.next()) {
270                (Some(left), Some(right)) => match left.composed_cmp(right) {
271                    cmp::Ordering::Equal => {}
272                    other => return other,
273                },
274                (None, None) => return cmp::Ordering::Equal,
275                _ => {
276                    // The root label sorts before any other label, so we
277                    // can never end up in a situation where one name runs
278                    // out of labels while comparing equal.
279                    unreachable!()
280                }
281            }
282        }
283    }
284
285    /// Returns the lowercase composed ordering.
286    fn lowercase_composed_cmp<N: ToDname + ?Sized>(
287        &self,
288        other: &N,
289    ) -> cmp::Ordering {
290        // Since there isn’t a `cmp_ignore_ascii_case` on slice, we don’t
291        // gain much from the shortcut.
292        let mut self_iter = self.iter_labels();
293        let mut other_iter = other.iter_labels();
294        loop {
295            match (self_iter.next(), other_iter.next()) {
296                (Some(left), Some(right)) => {
297                    match left.lowercase_composed_cmp(right) {
298                        cmp::Ordering::Equal => {}
299                        other => return other,
300                    }
301                }
302                (None, None) => return cmp::Ordering::Equal,
303                _ => {
304                    // The root label sorts before any other label, so we
305                    // can never end up in a situation where one name runs
306                    // out of labels while comparing equal.
307                    unreachable!()
308                }
309            }
310        }
311    }
312
313    /// Returns the number of labels for the RRSIG Labels field.
314    ///
315    /// This is the actual number of labels without counting the root label
316    /// or a possible initial asterisk label.
317    fn rrsig_label_count(&self) -> u8 {
318        let mut labels = self.iter_labels();
319        if labels.next().unwrap().is_wildcard() {
320            (labels.count() - 1) as u8
321        } else {
322            labels.count() as u8
323        }
324    }
325}
326
327impl<'a, N: ToDname + ?Sized + 'a> ToDname for &'a N {}
328
329//------------ ToRelativeDname -----------------------------------------------
330
331/// A type that represents a relative domain name.
332///
333/// In order to be a relative domain name, a type needs to be able to
334/// provide a sequence of labels via an iterator where the last label is not
335/// the root label. The type also needs to be able to compose the wire-format
336/// representation of the domain name it represents which must not be longer
337/// than 254 characters. This limit has been chosen so that by attaching the
338/// one character long root label, a valid absolute name can be constructed
339/// from the relative name.
340///
341/// The most important types implementing this trait are [`RelativeDname`]
342/// and [`Chain<L,R>`] where `R` is a `ToRelativeDname` itself.
343///
344/// [`Chain<L, R>`]: struct.Chain.html
345/// [`RelativeDname`]: struct.RelativeDname.html
346pub trait ToRelativeDname: ToLabelIter {
347    /// Converts the name into a single, continous name.
348    ///
349    /// The canonical implementation provided by the trait iterates over the
350    /// labels of the name and adds them one by one to [`RelativeDname`].
351    /// This will work for any name but an optimized implementation can be
352    /// provided for
353    /// some types of names.
354    ///
355    /// [`RelativeDname`]: struct.RelativeDname.html
356    fn to_relative_dname<Octets>(
357        &self,
358    ) -> Result<RelativeDname<Octets>, BuilderAppendError<Octets>>
359    where
360        Octets: FromBuilder,
361        <Octets as FromBuilder>::Builder: EmptyBuilder,
362    {
363        let mut builder =
364            Octets::Builder::with_capacity(self.compose_len().into());
365        self.iter_labels()
366            .try_for_each(|label| label.compose(&mut builder))?;
367        Ok(unsafe { RelativeDname::from_octets_unchecked(builder.freeze()) })
368    }
369
370    /// Converts the name into a single name in canonical form.
371    fn to_canonical_relative_dname<Octets>(
372        &self,
373    ) -> Result<RelativeDname<Octets>, BuilderAppendError<Octets>>
374    where
375        Octets: FromBuilder,
376        <Octets as FromBuilder>::Builder: EmptyBuilder,
377    {
378        let mut builder =
379            Octets::Builder::with_capacity(self.compose_len().into());
380        self.iter_labels()
381            .try_for_each(|label| label.compose_canonical(&mut builder))?;
382        Ok(unsafe { RelativeDname::from_octets_unchecked(builder.freeze()) })
383    }
384
385    /// Returns a byte slice of the content if possible.
386    ///
387    /// This method can is used to optimize comparision operations between
388    /// two values that are indeed flat names.
389    fn as_flat_slice(&self) -> Option<&[u8]> {
390        None
391    }
392
393    fn compose<Target: OctetsBuilder + ?Sized>(
394        &self,
395        target: &mut Target,
396    ) -> Result<(), Target::AppendError> {
397        if let Some(slice) = self.as_flat_slice() {
398            target.append_slice(slice)
399        } else {
400            for label in self.iter_labels() {
401                label.compose(target)?;
402            }
403            Ok(())
404        }
405    }
406
407    fn compose_canonical<Target: OctetsBuilder + ?Sized>(
408        &self,
409        target: &mut Target,
410    ) -> Result<(), Target::AppendError> {
411        for label in self.iter_labels() {
412            label.compose_canonical(target)?;
413        }
414        Ok(())
415    }
416
417    /// Returns a cow of the relative domain name.
418    ///
419    /// If the name is available as one single slice – i.e.,
420    /// [`as_flat_slice`] returns ‘some,’ creates the borrowed variant from
421    /// that slice. Otherwise assembles an owned variant via [`to_dname`].
422    ///
423    /// [`as_flat_slice`]: #method.as_flat_slice
424    /// [`to_dname`]: #method.to_dname
425    #[cfg(feature = "std")]
426    fn to_cow(&self) -> RelativeDname<std::borrow::Cow<[u8]>> {
427        let octets = self
428            .as_flat_slice()
429            .map(Cow::Borrowed)
430            .unwrap_or_else(|| Cow::Owned(self.to_vec().into_octets()));
431        unsafe { RelativeDname::from_octets_unchecked(octets) }
432    }
433
434    /// Returns the domain name assembled into a `Vec<u8>`.
435    #[cfg(feature = "std")]
436    fn to_vec(&self) -> RelativeDname<std::vec::Vec<u8>> {
437        infallible(self.to_relative_dname())
438    }
439
440    /// Returns the domain name assembled into a bytes value.
441    #[cfg(feature = "bytes")]
442    fn to_bytes(&self) -> RelativeDname<Bytes> {
443        infallible(self.to_relative_dname())
444    }
445
446    /// Returns whether the name is empty.
447    fn is_empty(&self) -> bool {
448        self.iter_labels().next().is_none()
449    }
450
451    /// Returns a chain of this name and the provided name.
452    fn chain<N: ToLabelIter>(
453        self,
454        suffix: N,
455    ) -> Result<Chain<Self, N>, LongChainError>
456    where
457        Self: Sized,
458    {
459        Chain::new(self, suffix)
460    }
461
462    /// Returns the absolute name by chaining it with the root label.
463    fn chain_root(self) -> Chain<Self, Dname<&'static [u8]>>
464    where
465        Self: Sized,
466    {
467        // Appending the root label will always work.
468        Chain::new(self, Dname::root()).unwrap()
469    }
470
471    /// Tests whether `self` and `other` are equal.
472    ///
473    /// This method can be used to implement `PartialEq` on types implementing
474    /// `ToDname` since a blanket implementation for all pairs of `ToDname`
475    /// is currently impossible.
476    ///
477    /// Domain names are compared ignoring ASCII case.
478    fn name_eq<N: ToRelativeDname + ?Sized>(&self, other: &N) -> bool {
479        if let (Some(left), Some(right)) =
480            (self.as_flat_slice(), other.as_flat_slice())
481        {
482            left.eq_ignore_ascii_case(right)
483        } else {
484            self.iter_labels().eq(other.iter_labels())
485        }
486    }
487
488    /// Returns the ordering between `self` and `other`.
489    ///
490    /// This method can be used to implement both `PartialOrd` and `Ord` on
491    /// types implementing `ToDname` since a blanket implementation for all
492    /// pairs of `ToDname`s is currently not possible.
493    ///
494    /// Domain name order is determined according to the ‘canonical DNS
495    /// name order’ as defined in [section 6.1 of RFC 4034][RFC4034-6.1].
496    /// This section describes how absolute domain names are ordered only.
497    /// We will order relative domain names according to these rules as if
498    /// they had the same origin, i.e., as if they were relative to the
499    /// same name.
500    ///
501    /// [RFC4034-6.1]: https://tools.ietf.org/html/rfc4034#section-6.1
502    fn name_cmp<N: ToRelativeDname + ?Sized>(
503        &self,
504        other: &N,
505    ) -> cmp::Ordering {
506        let mut self_iter = self.iter_labels();
507        let mut other_iter = other.iter_labels();
508        loop {
509            match (self_iter.next_back(), other_iter.next_back()) {
510                (Some(left), Some(right)) => match left.cmp(right) {
511                    cmp::Ordering::Equal => {}
512                    res => return res,
513                },
514                (None, Some(_)) => return cmp::Ordering::Less,
515                (Some(_), None) => return cmp::Ordering::Greater,
516                (None, None) => return cmp::Ordering::Equal,
517            }
518        }
519    }
520}
521
522impl<'a, N: ToRelativeDname + ?Sized + 'a> ToRelativeDname for &'a N {}
523
524//------------ FlattenInto ---------------------------------------------------
525
526pub trait FlattenInto<Target>: Sized {
527    type AppendError: Into<ShortBuf>;
528
529    fn try_flatten_into(self) -> Result<Target, Self::AppendError>;
530
531    fn flatten_into(self) -> Target
532    where
533        Self::AppendError: Into<Infallible>,
534    {
535        infallible(self.try_flatten_into())
536    }
537}