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>`] 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, SecurityAlgorithm};
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, mem, 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 [`SecurityAlgorithm`] 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 [`SecurityAlgorithm`] 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)]
50#[repr(transparent)]
51pub struct Understood<Variant, Octs: ?Sized> {
52    /// A marker for the variant.
53    marker: PhantomData<Variant>,
54
55    /// The octets with the data.
56    ///
57    /// These octets contain a sequence of composed [`SecurityAlgorithm`] values.
58    octets: Octs,
59}
60
61/// The marker type for the DAU option.
62///
63/// Use this as the `Variant` type argument of the
64/// [`Understood<..>`][Understood] type to select a DAU option.
65#[derive(Clone, Copy, Debug)]
66pub struct DauVariant;
67
68/// The marker type for the DHU option.
69///
70/// Use this as the `Variant` type argument of the
71/// [`Understood<..>`][Understood] type to select a DHU option.
72#[derive(Clone, Copy, Debug)]
73pub struct DhuVariant;
74
75/// The marker type for the N3U option.
76///
77/// Use this as the `Variant` type argument of the
78/// [`Understood<..>`][Understood] type to select a N3U option.
79#[derive(Clone, Copy, Debug)]
80pub struct N3uVariant;
81
82/// A type alias for the DAU option.
83pub type Dau<Octs> = Understood<DauVariant, Octs>;
84
85/// A type alias for the DHU option.
86pub type Dhu<Octs> = Understood<DhuVariant, Octs>;
87
88/// A type alias for the N3U option.
89pub type N3u<Octs> = Understood<N3uVariant, Octs>;
90
91impl<Variant, Octs> Understood<Variant, Octs> {
92    /// Creates a new value from an octets sequence.
93    ///
94    /// Returns an error if the slice does not contain a value in wire
95    /// format or is longer than 65,535 octets.
96    pub fn from_octets(octets: Octs) -> Result<Self, ParseError>
97    where
98        Octs: AsRef<[u8]>,
99    {
100        Understood::<Variant, _>::check_slice(octets.as_ref())?;
101        Ok(unsafe { Self::from_octets_unchecked(octets) })
102    }
103
104    /// Creates a new value from an octets sequence without checking.
105    ///
106    /// # Safety
107    ///
108    /// The caller needs to make sure that the slice contains a sequence of
109    /// 16 bit values that is no longer than 65,535 octets.
110    pub unsafe fn from_octets_unchecked(octets: Octs) -> Self {
111        Understood {
112            marker: PhantomData,
113            octets
114        }
115    }
116
117    /// Creates a new value from a sequence of algorithms.
118    ///
119    /// The operation will fail if the iterator returns more than 32,767
120    /// algorithms.
121    pub fn from_sec_algs(
122        sec_algs: impl IntoIterator<Item = SecurityAlgorithm>
123    ) -> Result<Self, BuildDataError>
124    where
125        Octs: FromBuilder,
126        <Octs as FromBuilder>::Builder: EmptyBuilder
127    {
128        let mut octets = EmptyBuilder::empty();
129        for item in sec_algs {
130            item.compose(&mut octets)?;
131        }
132        let octets = Octs::from_builder(octets);
133        LongOptData::check_len(octets.as_ref().len())?;
134        Ok(unsafe { Self::from_octets_unchecked(octets) })
135    }
136}
137
138impl<Variant> Understood<Variant, [u8]> {
139    /// Creates a new value from an octets slice.
140    ///
141    /// Returns an error if the slice does not contain a value in wire
142    /// format or is longer than 65,535 octets.
143    pub fn from_slice(slice: &[u8]) -> Result<&Self, ParseError> {
144        Understood::<Variant, _>::check_slice(slice)?;
145        Ok(unsafe { Self::from_slice_unchecked(slice) })
146    }
147
148    /// Creates a new value from an octets slice without checking.
149    ///
150    /// # Safety
151    ///
152    /// The caller needs to make sure that the slice contains a sequence of
153    /// 16 bit values that is no longer than 65,535 octets.
154    #[must_use]
155    pub unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
156        // SAFETY: Understood has repr(transparent)
157        mem::transmute(slice)
158    }
159
160    /// Checks that a slice contains a correctly encoded value.
161    fn check_slice(slice: &[u8]) -> Result<(), ParseError> {
162        LongOptData::check_len(slice.len())?;
163        if slice.len() % usize::from(u16::COMPOSE_LEN) != 0 {
164            return Err(ParseError::form_error("invalid understood data"))
165        }
166        Ok(())
167    }
168}
169
170impl<Variant, Octs: AsRef<[u8]>> Understood<Variant, Octs> {
171    /// Parses a value from its wire format.
172    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
173        parser: &mut Parser<'a, Src>,
174    ) -> Result<Self, ParseError> {
175        Self::from_octets(parser.parse_octets(parser.remaining())?)
176    }
177}
178
179impl<Variant, Octs: ?Sized> Understood<Variant, Octs> {
180    /// Returns a reference to the underlying octets.
181    pub fn as_octets(&self) -> &Octs {
182        &self.octets
183    }
184
185    /// Converts a value into its underlying octets.
186    pub fn into_octets(self) -> Octs
187    where
188        Octs: Sized,
189    {
190        self.octets
191    }
192
193    /// Returns the data as an octets slice.
194    pub fn as_slice(&self) -> &[u8]
195    where
196        Octs: AsRef<[u8]>,
197    {
198        self.octets.as_ref()
199    }
200
201    /// Returns a reference to a value over an octets slice.
202    pub fn for_slice(&self) -> &Understood<Variant, [u8]>
203    where
204        Octs: AsRef<[u8]>,
205    {
206        unsafe {
207            Understood::<Variant, _>::from_slice_unchecked(
208                self.octets.as_ref()
209            )
210        }
211    }
212
213    /// Returns an iterator over the algorithms in the data.
214    pub fn iter(&self) -> SecurityAlgorithmIter
215    where
216        Octs: AsRef<[u8]>,
217    {
218        SecurityAlgorithmIter::new(self.octets.as_ref())
219    }
220}
221
222impl Understood<DauVariant, ()> {
223    pub(super) const CODE: OptionCode = OptionCode::DAU;
224}
225
226impl Understood<DhuVariant, ()> {
227    pub(super) const CODE: OptionCode = OptionCode::DHU;
228}
229
230impl Understood<N3uVariant, ()> {
231    pub(super) const CODE: OptionCode = OptionCode::N3U;
232}
233
234//--- OctetsFrom
235
236impl<Variant, O, OO> OctetsFrom<Understood<Variant, O>>
237for Understood<Variant, OO>
238where
239    OO: OctetsFrom<O>,
240{
241    type Error = OO::Error;
242
243    fn try_octets_from(
244        source: Understood<Variant, O>
245    ) -> Result<Self, Self::Error> {
246        Ok(unsafe {
247            Self::from_octets_unchecked(
248                OO::try_octets_from(source.octets)?
249            )
250        })
251    }
252}
253
254//--- AsRef, AsMut, Borrow, BorrowMut
255
256impl<Variant, Octs> AsRef<[u8]> for Understood<Variant, Octs>
257where Octs: AsRef<[u8]> + ?Sized {
258    fn as_ref(&self) -> &[u8] {
259        self.as_slice()
260    }
261}
262
263impl<Variant, Octs> borrow::Borrow<[u8]> for Understood<Variant, Octs>
264where Octs: AsRef<[u8]> + ?Sized {
265    fn borrow(&self) -> &[u8] {
266        self.as_slice()
267    }
268}
269
270//--- PartialEq and Eq
271
272impl<Var, OtherVar, Octs, OtherOcts> PartialEq<Understood<OtherVar, OtherOcts>>
273for Understood<Var, Octs>
274where
275    Octs: AsRef<[u8]> + ?Sized,
276    OtherOcts: AsRef<[u8]> + ?Sized,
277{
278    fn eq(&self, other: &Understood<OtherVar, OtherOcts>) -> bool {
279        self.as_slice().eq(other.as_slice())
280    }
281}
282
283impl<Variant, Octs: AsRef<[u8]> + ?Sized> Eq for Understood<Variant, Octs> { }
284
285//--- Hash
286
287impl<Variant, Octs: AsRef<[u8]>> hash::Hash for Understood<Variant, Octs> {
288    fn hash<H: hash::Hasher>(&self, state: &mut H) {
289        self.as_slice().hash(state)
290    }
291}
292
293//--- OptData etc.
294
295impl<Octs: ?Sized> OptData for Understood<DauVariant, Octs> {
296    fn code(&self) -> OptionCode {
297        OptionCode::DAU
298    }
299}
300
301impl<Octs: ?Sized> OptData for Understood<DhuVariant, Octs> {
302    fn code(&self) -> OptionCode {
303        OptionCode::DHU
304    }
305}
306
307impl<Octs: ?Sized> OptData for Understood<N3uVariant, Octs> {
308    fn code(&self) -> OptionCode {
309        OptionCode::N3U
310    }
311}
312
313impl<'a, Octs: Octets + ?Sized> ParseOptData<'a, Octs>
314for Understood<DauVariant, Octs::Range<'a>> {
315    fn parse_option(
316        code: OptionCode,
317        parser: &mut Parser<'a, Octs>,
318    ) -> Result<Option<Self>, ParseError> {
319        if code == OptionCode::DAU {
320            Self::parse(parser).map(Some)
321        }
322        else {
323            Ok(None)
324        }
325    }
326}
327
328impl<'a, Octs: Octets + ?Sized> ParseOptData<'a, Octs>
329for Understood<DhuVariant, Octs::Range<'a>> {
330    fn parse_option(
331        code: OptionCode,
332        parser: &mut Parser<'a, Octs>,
333    ) -> Result<Option<Self>, ParseError> {
334        if code == OptionCode::DHU {
335            Self::parse(parser).map(Some)
336        }
337        else {
338            Ok(None)
339        }
340    }
341}
342
343impl<'a, Octs: Octets + ?Sized> ParseOptData<'a, Octs>
344for Understood<N3uVariant, Octs::Range<'a>> {
345    fn parse_option(
346        code: OptionCode,
347        parser: &mut Parser<'a, Octs>,
348    ) -> Result<Option<Self>, ParseError> {
349        if code == OptionCode::N3U {
350            Self::parse(parser).map(Some)
351        }
352        else {
353            Ok(None)
354        }
355    }
356}
357
358impl<Variant, Octs> ComposeOptData for Understood<Variant, Octs>
359where
360    Self: OptData,
361    Octs: AsRef<[u8]> + ?Sized, 
362{
363    fn compose_len(&self) -> u16 {
364        self.octets.as_ref().len().try_into().expect("long option data")
365    }
366
367    fn compose_option<Target: OctetsBuilder + ?Sized>(
368        &self, target: &mut Target
369    ) -> Result<(), Target::AppendError> {
370        target.append_slice(self.octets.as_ref())
371    }
372}
373
374//--- IntoIter
375
376impl<'a, Variant, Octs> IntoIterator for &'a Understood<Variant, Octs>
377where
378    Octs: AsRef<[u8]> + ?Sized
379{
380    type Item = SecurityAlgorithm;
381    type IntoIter = SecurityAlgorithmIter<'a>;
382
383    fn into_iter(self) -> Self::IntoIter {
384        self.iter()
385    }
386}
387
388//--- Display
389
390impl<Variant, Octs> fmt::Display for Understood<Variant, Octs>
391where
392    Octs: AsRef<[u8]> + ?Sized,
393{
394    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
395        let mut first = true;
396
397        for v in self.octets.as_ref() {
398            if first {
399                write!(f, "{}", *v)?;
400                first = false;
401            } else {
402                write!(f, ", {}", *v)?
403            }
404        }
405        Ok(())
406    }
407}
408
409//--- Debug
410
411impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Understood<DauVariant, Octs> {
412    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
413        f.debug_tuple("Understood<DauVariant>")
414            .field(&format_args!("{}", self))
415            .finish()
416    }
417}
418
419impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Understood<DhuVariant, Octs> {
420    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
421        f.debug_tuple("Understood<DhuVariant>")
422            .field(&format_args!("{}", self))
423            .finish()
424    }
425}
426
427impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Understood<N3uVariant, Octs> {
428    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
429        f.debug_tuple("Understood<N3uVariant>")
430            .field(&format_args!("{}", self))
431            .finish()
432    }
433}
434
435//--- Serialize
436
437#[cfg(feature = "serde")]
438impl<V, Octs: AsRef<[u8]>> serde::Serialize for Understood<V, Octs> {
439    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
440    where
441        S: serde::Serializer {
442        use serde::ser::SerializeSeq;
443        let mut list = serializer.serialize_seq(None)?;
444        for item in self.iter() {
445            list.serialize_element(&item)?;
446        }
447        list.end()
448    }
449}
450
451//--- Extended Opt and OptBuilder
452
453impl<Octs: Octets> Opt<Octs> {
454    /// Returns the first DAU option if present.
455    ///
456    /// This option lists the DNSSEC signature algorithms the requester
457    /// supports.
458    pub fn dau(&self) -> Option<Dau<Octs::Range<'_>>> {
459        self.first()
460    }
461
462    /// Returns the first DHU option if present.
463    ///
464    /// This option lists the DS hash algorithms the requester supports.
465    pub fn dhu(&self) -> Option<Dhu<Octs::Range<'_>>> {
466        self.first()
467    }
468
469    /// Returns the first N3U option if present.
470    ///
471    /// This option lists the NSEC3 hash algorithms the requester supports.
472    pub fn n3u(&self) -> Option<N3u<Octs::Range<'_>>> {
473        self.first()
474    }
475}
476
477impl<Target: Composer> OptBuilder<'_, Target> {
478    /// Appends a DAU option.
479    ///
480    /// The DAU option lists the DNSSEC signature algorithms the requester
481    /// supports.
482    pub fn dau(
483        &mut self, algs: &impl AsRef<[SecurityAlgorithm]>,
484    ) -> Result<(), BuildDataError> {
485        Ok(self.push_raw_option(
486            OptionCode::DAU,
487            u16::try_from(
488                algs.as_ref().len() * usize::from(SecurityAlgorithm::COMPOSE_LEN)
489            ).map_err(|_| BuildDataError::LongOptData)?,
490            |octs| {
491                algs.as_ref().iter().try_for_each(|item| item.compose(octs))
492            },
493        )?)
494    }
495
496    /// Appends a DHU option.
497    ///
498    /// The DHU option lists the DS hash algorithms the requester supports.
499    pub fn dhu(
500        &mut self, algs: &impl AsRef<[SecurityAlgorithm]>,
501    ) -> Result<(), BuildDataError> {
502        Ok(self.push_raw_option(
503            OptionCode::DHU,
504            u16::try_from(
505                algs.as_ref().len() * usize::from(SecurityAlgorithm::COMPOSE_LEN)
506            ).map_err(|_| BuildDataError::LongOptData)?,
507            |octs| {
508                algs.as_ref().iter().try_for_each(|item| item.compose(octs))
509            },
510        )?)
511    }
512
513    /// Appends a N3U option.
514    ///
515    /// The N3U option lists the NSEC3 hash algorithms the requester supports.
516    pub fn n3u(
517        &mut self, algs: &impl AsRef<[SecurityAlgorithm]>,
518    ) -> Result<(), BuildDataError> {
519        Ok(self.push_raw_option(
520            OptionCode::N3U,
521            u16::try_from(
522                algs.as_ref().len() * usize::from(SecurityAlgorithm::COMPOSE_LEN)
523            ).map_err(|_| BuildDataError::LongOptData)?,
524            |octs| {
525                algs.as_ref().iter().try_for_each(|item| item.compose(octs))
526            },
527        )?)
528    }
529}
530
531//------------ SecurityAlgorithmsIter ----------------------------------------
532
533pub struct SecurityAlgorithmIter<'a>(slice::Iter<'a, u8>);
534
535impl<'a> SecurityAlgorithmIter<'a> {
536    fn new(slice: &'a [u8]) -> Self {
537        SecurityAlgorithmIter(slice.iter())
538    }
539}
540
541impl Iterator for SecurityAlgorithmIter<'_> {
542    type Item = SecurityAlgorithm;
543
544    fn next(&mut self) -> Option<Self::Item> {
545        self.0.next().map(|x| SecurityAlgorithm::from_int(*x))
546    }
547}
548
549//============ Tests ========================================================
550
551#[cfg(test)]
552#[cfg(all(feature = "std", feature = "bytes"))]
553mod test {
554    use super::*;
555    use super::super::test::test_option_compose_parse;
556
557    #[test]
558    #[allow(clippy::redundant_closure)] // lifetimes ...
559    fn dau_compose_parse() {
560        test_option_compose_parse(
561            &Dau::from_octets("foof").unwrap(),
562            |parser| Dau::parse(parser)
563        );
564    }
565}