domain/base/opt/
algsig.rs

1//! EDNS options for signaling cryptographic algorithm understanding.
2//!
3//! The options in this module allow a validating resolver to signal which
4//! signature and hash algorithms they support when making queries.  These
5//! options are defined in [RFC 6975].
6//!
7//! There are three options for three different purposes. However, the data
8//! for each of them is a sequence of security algorithms. The module only
9//! defines one type [`Understood<Variant, Octs>`][Understood] which carries
10//! the specific variant as its first type parameter. Marker types and
11//! type aliases are defined for the three options [Dau], [Dhu], and [N3u]
12//! which specific the DNSSEC signature algorithms, DS hash algorithm, and
13//! NSEC3 hash algorithms understood by the client, respectively.
14//!
15//! [RFC 6975]: https://tools.ietf.org/html/rfc6975
16
17use super::super::iana::{OptionCode, SecAlg};
18use super::super::message_builder::OptBuilder;
19use super::super::wire::{Compose, Composer, ParseError};
20use super::{
21    BuildDataError, OptData, ComposeOptData, LongOptData, Opt, ParseOptData
22};
23use octseq::builder::{EmptyBuilder, FromBuilder, OctetsBuilder};
24use octseq::octets::{Octets, OctetsFrom};
25use octseq::parse::Parser;
26use core::{borrow, fmt, hash, slice};
27use core::marker::PhantomData;
28
29
30//------------ Understood ----------------------------------------------------
31
32/// Option data for understood DNSSEC algorithms.
33///
34/// This type provides the option data for the three options DAU, DHU, and
35/// N3U which allow a client to specify the cryptographic algorithms it
36/// supports for DNSSEC signatures, DS hashes, and NSEC3 hashes respectively.
37/// Each of them contains a sequence of [`SecAlg`] values in wire format.
38///
39/// Which exact option is to be used is specified via the `Variant` type
40/// argument. Three marker types `DauVariant`, `DhuVariant` and `N3uVariant`
41/// are defined with accompanying type aliases [`Dau`], [`Dhu`], and [`N3u`].
42///
43/// You can create a new value from anything that can be turned into an
44/// iterator over [`SecAlg`] via the
45/// [`from_sec_algs`][Understood::from_sec_algs] associated function.
46/// Once you have a value, you can iterate over the algorithms via the
47/// [`iter`][Understood::iter] method or use the `IntoIterator` implementation
48/// for a reference.
49#[derive(Clone, Copy, Debug)]
50pub struct Understood<Variant, Octs: ?Sized> {
51    /// A marker for the variant.
52    marker: PhantomData<Variant>,
53
54    /// The octets with the data.
55    ///
56    /// These octets contain a sequence of composed [`SecAlg`] values.
57    octets: Octs,
58}
59
60/// The marker type for the DAU option.
61///
62/// Use this as the `Variant` type argument of the
63/// [`Understood<..>`][Understood] type to select a DAU option.
64#[derive(Clone, Copy, Debug)]
65pub struct DauVariant;
66
67/// The marker type for the DHU option.
68///
69/// Use this as the `Variant` type argument of the
70/// [`Understood<..>`][Understood] type to select a DHU option.
71#[derive(Clone, Copy, Debug)]
72pub struct DhuVariant;
73
74/// The marker type for the N3U option.
75///
76/// Use this as the `Variant` type argument of the
77/// [`Understood<..>`][Understood] type to select a N3U option.
78#[derive(Clone, Copy, Debug)]
79pub struct N3uVariant;
80
81/// A type alias for the DAU option.
82pub type Dau<Octs> = Understood<DauVariant, Octs>;
83
84/// A type alias for the DHU option.
85pub type Dhu<Octs> = Understood<DhuVariant, Octs>;
86
87/// A type alias for the N3U option.
88pub type N3u<Octs> = Understood<N3uVariant, Octs>;
89
90impl<Variant, Octs> Understood<Variant, Octs> {
91    /// Creates a new value from an octets sequence.
92    ///
93    /// Returns an error if the slice does not contain a value in wire
94    /// format or is longer than 65,535 octets.
95    pub fn from_octets(octets: Octs) -> Result<Self, ParseError>
96    where
97        Octs: AsRef<[u8]>,
98    {
99        Understood::<Variant, _>::check_slice(octets.as_ref())?;
100        Ok(unsafe { Self::from_octets_unchecked(octets) })
101    }
102
103    /// Creates a new value from an octets sequence without checking.
104    ///
105    /// # Safety
106    ///
107    /// The caller needs to make sure that the slice contains a sequence of
108    /// 16 bit values that is no longer than 65,535 octets.
109    pub unsafe fn from_octets_unchecked(octets: Octs) -> Self {
110        Understood {
111            marker: PhantomData,
112            octets
113        }
114    }
115
116    /// Creates a new value from a sequence of algorithms.
117    ///
118    /// The operation will fail if the iterator returns more than 32,767
119    /// algorithms.
120    pub fn from_sec_algs(
121        sec_algs: impl IntoIterator<Item = SecAlg>
122    ) -> Result<Self, BuildDataError>
123    where
124        Octs: FromBuilder,
125        <Octs as FromBuilder>::Builder: EmptyBuilder
126    {
127        let mut octets = EmptyBuilder::empty();
128        for item in sec_algs {
129            item.compose(&mut octets)?;
130        }
131        let octets = Octs::from_builder(octets);
132        LongOptData::check_len(octets.as_ref().len())?;
133        Ok(unsafe { Self::from_octets_unchecked(octets) })
134    }
135}
136
137impl<Variant> Understood<Variant, [u8]> {
138    /// Creates a new value from an octets slice.
139    ///
140    /// Returns an error if the slice does not contain a value in wire
141    /// format or is longer than 65,535 octets.
142    pub fn from_slice(slice: &[u8]) -> Result<&Self, ParseError> {
143        Understood::<Variant, _>::check_slice(slice)?;
144        Ok(unsafe { Self::from_slice_unchecked(slice) })
145    }
146
147    /// Creates a new value from an octets slice without checking.
148    ///
149    /// # Safety
150    ///
151    /// The caller needs to make sure that the slice contains a sequence of
152    /// 16 bit values that is no longer than 65,535 octets.
153    #[must_use]
154    pub unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
155        &*(slice as *const [u8] as *const Self)
156    }
157
158    /// Checks that a slice contains a correctly encoded value.
159    fn check_slice(slice: &[u8]) -> Result<(), ParseError> {
160        LongOptData::check_len(slice.len())?;
161        if slice.len() % usize::from(u16::COMPOSE_LEN) != 0 {
162            return Err(ParseError::form_error("invalid understood data"))
163        }
164        Ok(())
165    }
166}
167
168impl<Variant, Octs: AsRef<[u8]>> Understood<Variant, Octs> {
169    /// Parses a value from its wire format.
170    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
171        parser: &mut Parser<'a, Src>,
172    ) -> Result<Self, ParseError> {
173        Self::from_octets(parser.parse_octets(parser.remaining())?)
174    }
175}
176
177impl<Variant, Octs: ?Sized> Understood<Variant, Octs> {
178    /// Returns a reference to the underlying octets.
179    pub fn as_octets(&self) -> &Octs {
180        &self.octets
181    }
182
183    /// Converts a value into its underlying octets.
184    pub fn into_octets(self) -> Octs
185    where
186        Octs: Sized,
187    {
188        self.octets
189    }
190
191    /// Returns the data as an octets slice.
192    pub fn as_slice(&self) -> &[u8]
193    where
194        Octs: AsRef<[u8]>,
195    {
196        self.octets.as_ref()
197    }
198
199    /// Returns a reference to a value over an octets slice.
200    pub fn for_slice(&self) -> &Understood<Variant, [u8]>
201    where
202        Octs: AsRef<[u8]>,
203    {
204        unsafe {
205            Understood::<Variant, _>::from_slice_unchecked(
206                self.octets.as_ref()
207            )
208        }
209    }
210
211    /// Returns an iterator over the algorithms in the data.
212    pub fn iter(&self) -> SecAlgsIter
213    where
214        Octs: AsRef<[u8]>,
215    {
216        SecAlgsIter::new(self.octets.as_ref())
217    }
218}
219
220//--- OctetsFrom
221
222impl<Variant, O, OO> OctetsFrom<Understood<Variant, O>>
223for Understood<Variant, OO>
224where
225    OO: OctetsFrom<O>,
226{
227    type Error = OO::Error;
228
229    fn try_octets_from(
230        source: Understood<Variant, O>
231    ) -> Result<Self, Self::Error> {
232        Ok(unsafe {
233            Self::from_octets_unchecked(
234                OO::try_octets_from(source.octets)?
235            )
236        })
237    }
238}
239
240//--- AsRef, AsMut, Borrow, BorrowMut
241
242impl<Variant, Octs> AsRef<[u8]> for Understood<Variant, Octs>
243where Octs: AsRef<[u8]> + ?Sized {
244    fn as_ref(&self) -> &[u8] {
245        self.as_slice()
246    }
247}
248
249impl<Variant, Octs> borrow::Borrow<[u8]> for Understood<Variant, Octs>
250where Octs: AsRef<[u8]> + ?Sized {
251    fn borrow(&self) -> &[u8] {
252        self.as_slice()
253    }
254}
255
256//--- PartialEq and Eq
257
258impl<Var, OtherVar, Octs, OtherOcts> PartialEq<Understood<OtherVar, OtherOcts>>
259for Understood<Var, Octs>
260where
261    Octs: AsRef<[u8]> + ?Sized,
262    OtherOcts: AsRef<[u8]> + ?Sized,
263{
264    fn eq(&self, other: &Understood<OtherVar, OtherOcts>) -> bool {
265        self.as_slice().eq(other.as_slice())
266    }
267}
268
269impl<Variant, Octs: AsRef<[u8]> + ?Sized> Eq for Understood<Variant, Octs> { }
270
271//--- Hash
272
273impl<Variant, Octs: AsRef<[u8]>> hash::Hash for Understood<Variant, Octs> {
274    fn hash<H: hash::Hasher>(&self, state: &mut H) {
275        self.as_slice().hash(state)
276    }
277}
278
279//--- OptData etc.
280
281impl<Octs: ?Sized> OptData for Understood<DauVariant, Octs> {
282    fn code(&self) -> OptionCode {
283        OptionCode::Dau
284    }
285}
286
287impl<Octs: ?Sized> OptData for Understood<DhuVariant, Octs> {
288    fn code(&self) -> OptionCode {
289        OptionCode::Dhu
290    }
291}
292
293impl<Octs: ?Sized> OptData for Understood<N3uVariant, Octs> {
294    fn code(&self) -> OptionCode {
295        OptionCode::N3u
296    }
297}
298
299impl<'a, Octs: Octets + ?Sized> ParseOptData<'a, Octs>
300for Understood<DauVariant, Octs::Range<'a>> {
301    fn parse_option(
302        code: OptionCode,
303        parser: &mut Parser<'a, Octs>,
304    ) -> Result<Option<Self>, ParseError> {
305        if code == OptionCode::Dau {
306            Self::parse(parser).map(Some)
307        }
308        else {
309            Ok(None)
310        }
311    }
312}
313
314impl<'a, Octs: Octets + ?Sized> ParseOptData<'a, Octs>
315for Understood<DhuVariant, Octs::Range<'a>> {
316    fn parse_option(
317        code: OptionCode,
318        parser: &mut Parser<'a, Octs>,
319    ) -> Result<Option<Self>, ParseError> {
320        if code == OptionCode::Dhu {
321            Self::parse(parser).map(Some)
322        }
323        else {
324            Ok(None)
325        }
326    }
327}
328
329impl<'a, Octs: Octets + ?Sized> ParseOptData<'a, Octs>
330for Understood<N3uVariant, Octs::Range<'a>> {
331    fn parse_option(
332        code: OptionCode,
333        parser: &mut Parser<'a, Octs>,
334    ) -> Result<Option<Self>, ParseError> {
335        if code == OptionCode::N3u {
336            Self::parse(parser).map(Some)
337        }
338        else {
339            Ok(None)
340        }
341    }
342}
343
344impl<Variant, Octs> ComposeOptData for Understood<Variant, Octs>
345where
346    Self: OptData,
347    Octs: AsRef<[u8]> + ?Sized, 
348{
349    fn compose_len(&self) -> u16 {
350        self.octets.as_ref().len().try_into().expect("long option data")
351    }
352
353    fn compose_option<Target: OctetsBuilder + ?Sized>(
354        &self, target: &mut Target
355    ) -> Result<(), Target::AppendError> {
356        target.append_slice(self.octets.as_ref())
357    }
358}
359
360//--- IntoIter
361
362impl<'a, Variant, Octs> IntoIterator for &'a Understood<Variant, Octs>
363where
364    Octs: AsRef<[u8]> + ?Sized
365{
366    type Item = SecAlg;
367    type IntoIter = SecAlgsIter<'a>;
368
369    fn into_iter(self) -> Self::IntoIter {
370        self.iter()
371    }
372}
373
374//--- Display
375
376impl<Variant, Octs> fmt::Display for Understood<Variant, Octs>
377where
378    Octs: AsRef<[u8]> + ?Sized,
379{
380    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
381        let mut first = true;
382
383        for v in self.octets.as_ref() {
384            if first {
385                write!(f, "{}", *v)?;
386                first = false;
387            } else {
388                write!(f, ", {}", *v)?
389            }
390        }
391        Ok(())
392    }
393}
394
395//--- Extended Opt and OptBuilder
396
397impl<Octs: Octets> Opt<Octs> {
398    /// Returns the first DAU option if present.
399    ///
400    /// This option lists the DNSSEC signature algorithms the requester
401    /// supports.
402    pub fn dau(&self) -> Option<Dau<Octs::Range<'_>>> {
403        self.first()
404    }
405
406    /// Returns the first DHU option if present.
407    ///
408    /// This option lists the DS hash algorithms the requester supports.
409    pub fn dhu(&self) -> Option<Dhu<Octs::Range<'_>>> {
410        self.first()
411    }
412
413    /// Returns the first N3U option if present.
414    ///
415    /// This option lists the NSEC3 hash algorithms the requester supports.
416    pub fn n3u(&self) -> Option<N3u<Octs::Range<'_>>> {
417        self.first()
418    }
419}
420
421impl<'a, Target: Composer> OptBuilder<'a, Target> {
422    /// Appends a DAU option.
423    ///
424    /// The DAU option lists the DNSSEC signature algorithms the requester
425    /// supports.
426    pub fn dau(
427        &mut self, algs: &impl AsRef<[SecAlg]>,
428    ) -> Result<(), BuildDataError> {
429        Ok(self.push_raw_option(
430            OptionCode::Dau,
431            u16::try_from(
432                algs.as_ref().len() * usize::from(SecAlg::COMPOSE_LEN)
433            ).map_err(|_| BuildDataError::LongOptData)?,
434            |octs| {
435                algs.as_ref().iter().try_for_each(|item| item.compose(octs))
436            },
437        )?)
438    }
439
440    /// Appends a DHU option.
441    ///
442    /// The DHU option lists the DS hash algorithms the requester supports.
443    pub fn dhu(
444        &mut self, algs: &impl AsRef<[SecAlg]>,
445    ) -> Result<(), BuildDataError> {
446        Ok(self.push_raw_option(
447            OptionCode::Dhu,
448            u16::try_from(
449                algs.as_ref().len() * usize::from(SecAlg::COMPOSE_LEN)
450            ).map_err(|_| BuildDataError::LongOptData)?,
451            |octs| {
452                algs.as_ref().iter().try_for_each(|item| item.compose(octs))
453            },
454        )?)
455    }
456
457    /// Appends a N3U option.
458    ///
459    /// The N3U option lists the NSEC3 hash algorithms the requester supports.
460    pub fn n3u(
461        &mut self, algs: &impl AsRef<[SecAlg]>,
462    ) -> Result<(), BuildDataError> {
463        Ok(self.push_raw_option(
464            OptionCode::N3u,
465            u16::try_from(
466                algs.as_ref().len() * usize::from(SecAlg::COMPOSE_LEN)
467            ).map_err(|_| BuildDataError::LongOptData)?,
468            |octs| {
469                algs.as_ref().iter().try_for_each(|item| item.compose(octs))
470            },
471        )?)
472    }
473}
474
475//------------ SecAlgsIter ---------------------------------------------------
476
477pub struct SecAlgsIter<'a>(slice::Iter<'a, u8>);
478
479impl<'a> SecAlgsIter<'a> {
480    fn new(slice: &'a [u8]) -> Self {
481        SecAlgsIter(slice.iter())
482    }
483}
484
485impl<'a> Iterator for SecAlgsIter<'a> {
486    type Item = SecAlg;
487
488    fn next(&mut self) -> Option<Self::Item> {
489        self.0.next().map(|x| SecAlg::from_int(*x))
490    }
491}
492
493//============ Tests ========================================================
494
495#[cfg(test)]
496#[cfg(all(feature = "std", feature = "bytes"))]
497mod test {
498    use super::*;
499    use super::super::test::test_option_compose_parse;
500
501    #[test]
502    #[allow(clippy::redundant_closure)] // lifetimes ...
503    fn dau_compose_parse() {
504        test_option_compose_parse(
505            &Dau::from_octets("foof").unwrap(),
506            |parser| Dau::parse(parser)
507        );
508    }
509}