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, ComposeOptData, LongOptData, Opt, OptData, ParseOptData,
22};
23use core::marker::PhantomData;
24use core::{borrow, fmt, hash, mem, slice};
25use octseq::builder::{EmptyBuilder, FromBuilder, OctetsBuilder};
26use octseq::octets::{Octets, OctetsFrom};
27use octseq::parse::Parser;
28
29//------------ Understood ----------------------------------------------------
30
31/// Option data for understood DNSSEC algorithms.
32///
33/// This type provides the option data for the three options DAU, DHU, and
34/// N3U which allow a client to specify the cryptographic algorithms it
35/// supports for DNSSEC signatures, DS hashes, and NSEC3 hashes respectively.
36/// Each of them contains a sequence of [`SecurityAlgorithm`] values in wire format.
37///
38/// Which exact option is to be used is specified via the `Variant` type
39/// argument. Three marker types `DauVariant`, `DhuVariant` and `N3uVariant`
40/// are defined with accompanying type aliases [`Dau`], [`Dhu`], and [`N3u`].
41///
42/// You can create a new value from anything that can be turned into an
43/// iterator over [`SecurityAlgorithm`] via the
44/// [`from_sec_algs`][Understood::from_sec_algs] associated function.
45/// Once you have a value, you can iterate over the algorithms via the
46/// [`iter`][Understood::iter] method or use the [`IntoIterator`] implementation
47/// for a reference.
48#[derive(Clone, Copy)]
49#[repr(transparent)]
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 [`SecurityAlgorithm`] 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 = SecurityAlgorithm>,
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        // SAFETY: Understood has repr(transparent)
156        mem::transmute(slice)
157    }
158
159    /// Checks that a slice contains a correctly encoded value.
160    fn check_slice(slice: &[u8]) -> Result<(), ParseError> {
161        LongOptData::check_len(slice.len())?;
162        if slice.len() % usize::from(u16::COMPOSE_LEN) != 0 {
163            return Err(ParseError::form_error("invalid understood data"));
164        }
165        Ok(())
166    }
167}
168
169impl<Variant, Octs: AsRef<[u8]>> Understood<Variant, Octs> {
170    /// Parses a value from its wire format.
171    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
172        parser: &mut Parser<'a, Src>,
173    ) -> Result<Self, ParseError> {
174        Self::from_octets(parser.parse_octets(parser.remaining())?)
175    }
176}
177
178impl<Variant, Octs: ?Sized> Understood<Variant, Octs> {
179    /// Returns a reference to the underlying octets.
180    pub fn as_octets(&self) -> &Octs {
181        &self.octets
182    }
183
184    /// Converts a value into its underlying octets.
185    pub fn into_octets(self) -> Octs
186    where
187        Octs: Sized,
188    {
189        self.octets
190    }
191
192    /// Returns the data as an octets slice.
193    pub fn as_slice(&self) -> &[u8]
194    where
195        Octs: AsRef<[u8]>,
196    {
197        self.octets.as_ref()
198    }
199
200    /// Returns a reference to a value over an octets slice.
201    pub fn for_slice(&self) -> &Understood<Variant, [u8]>
202    where
203        Octs: AsRef<[u8]>,
204    {
205        unsafe {
206            Understood::<Variant, _>::from_slice_unchecked(
207                self.octets.as_ref(),
208            )
209        }
210    }
211
212    /// Returns an iterator over the algorithms in the data.
213    pub fn iter(&self) -> SecurityAlgorithmIter<'_>
214    where
215        Octs: AsRef<[u8]>,
216    {
217        SecurityAlgorithmIter::new(self.octets.as_ref())
218    }
219}
220
221impl Understood<DauVariant, ()> {
222    pub(super) const CODE: OptionCode = OptionCode::DAU;
223}
224
225impl Understood<DhuVariant, ()> {
226    pub(super) const CODE: OptionCode = OptionCode::DHU;
227}
228
229impl Understood<N3uVariant, ()> {
230    pub(super) const CODE: OptionCode = OptionCode::N3U;
231}
232
233//--- OctetsFrom
234
235impl<Variant, O, OO> OctetsFrom<Understood<Variant, O>>
236    for Understood<Variant, OO>
237where
238    OO: OctetsFrom<O>,
239{
240    type Error = OO::Error;
241
242    fn try_octets_from(
243        source: Understood<Variant, O>,
244    ) -> Result<Self, Self::Error> {
245        Ok(unsafe {
246            Self::from_octets_unchecked(OO::try_octets_from(source.octets)?)
247        })
248    }
249}
250
251//--- AsRef, AsMut, Borrow, BorrowMut
252
253impl<Variant, Octs> AsRef<[u8]> for Understood<Variant, Octs>
254where
255    Octs: AsRef<[u8]> + ?Sized,
256{
257    fn as_ref(&self) -> &[u8] {
258        self.as_slice()
259    }
260}
261
262impl<Variant, Octs> borrow::Borrow<[u8]> for Understood<Variant, Octs>
263where
264    Octs: AsRef<[u8]> + ?Sized,
265{
266    fn borrow(&self) -> &[u8] {
267        self.as_slice()
268    }
269}
270
271//--- PartialEq and Eq
272
273impl<Var, OtherVar, Octs, OtherOcts>
274    PartialEq<Understood<OtherVar, OtherOcts>> for Understood<Var, Octs>
275where
276    Octs: AsRef<[u8]> + ?Sized,
277    OtherOcts: AsRef<[u8]> + ?Sized,
278{
279    fn eq(&self, other: &Understood<OtherVar, OtherOcts>) -> bool {
280        self.as_slice().eq(other.as_slice())
281    }
282}
283
284impl<Variant, Octs: AsRef<[u8]> + ?Sized> Eq for Understood<Variant, Octs> {}
285
286//--- Hash
287
288impl<Variant, Octs: AsRef<[u8]>> hash::Hash for Understood<Variant, Octs> {
289    fn hash<H: hash::Hasher>(&self, state: &mut H) {
290        self.as_slice().hash(state)
291    }
292}
293
294//--- OptData etc.
295
296impl<Octs: ?Sized> OptData for Understood<DauVariant, Octs> {
297    fn code(&self) -> OptionCode {
298        OptionCode::DAU
299    }
300}
301
302impl<Octs: ?Sized> OptData for Understood<DhuVariant, Octs> {
303    fn code(&self) -> OptionCode {
304        OptionCode::DHU
305    }
306}
307
308impl<Octs: ?Sized> OptData for Understood<N3uVariant, Octs> {
309    fn code(&self) -> OptionCode {
310        OptionCode::N3U
311    }
312}
313
314impl<'a, Octs: Octets + ?Sized> ParseOptData<'a, Octs>
315    for Understood<DauVariant, Octs::Range<'a>>
316{
317    fn parse_option(
318        code: OptionCode,
319        parser: &mut Parser<'a, Octs>,
320    ) -> Result<Option<Self>, ParseError> {
321        if code == OptionCode::DAU {
322            Self::parse(parser).map(Some)
323        } else {
324            Ok(None)
325        }
326    }
327}
328
329impl<'a, Octs: Octets + ?Sized> ParseOptData<'a, Octs>
330    for Understood<DhuVariant, Octs::Range<'a>>
331{
332    fn parse_option(
333        code: OptionCode,
334        parser: &mut Parser<'a, Octs>,
335    ) -> Result<Option<Self>, ParseError> {
336        if code == OptionCode::DHU {
337            Self::parse(parser).map(Some)
338        } else {
339            Ok(None)
340        }
341    }
342}
343
344impl<'a, Octs: Octets + ?Sized> ParseOptData<'a, Octs>
345    for Understood<N3uVariant, Octs::Range<'a>>
346{
347    fn parse_option(
348        code: OptionCode,
349        parser: &mut Parser<'a, Octs>,
350    ) -> Result<Option<Self>, ParseError> {
351        if code == OptionCode::N3U {
352            Self::parse(parser).map(Some)
353        } else {
354            Ok(None)
355        }
356    }
357}
358
359impl<Variant, Octs> ComposeOptData for Understood<Variant, Octs>
360where
361    Self: OptData,
362    Octs: AsRef<[u8]> + ?Sized,
363{
364    fn compose_len(&self) -> u16 {
365        self.octets
366            .as_ref()
367            .len()
368            .try_into()
369            .expect("long option data")
370    }
371
372    fn compose_option<Target: OctetsBuilder + ?Sized>(
373        &self,
374        target: &mut Target,
375    ) -> Result<(), Target::AppendError> {
376        target.append_slice(self.octets.as_ref())
377    }
378}
379
380//--- IntoIter
381
382impl<'a, Variant, Octs> IntoIterator for &'a Understood<Variant, Octs>
383where
384    Octs: AsRef<[u8]> + ?Sized,
385{
386    type Item = SecurityAlgorithm;
387    type IntoIter = SecurityAlgorithmIter<'a>;
388
389    fn into_iter(self) -> Self::IntoIter {
390        self.iter()
391    }
392}
393
394//--- Display
395
396impl<Variant, Octs> fmt::Display for Understood<Variant, Octs>
397where
398    Octs: AsRef<[u8]> + ?Sized,
399{
400    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401        let mut first = true;
402
403        for v in self.octets.as_ref() {
404            if first {
405                write!(f, "{}", *v)?;
406                first = false;
407            } else {
408                write!(f, ", {}", *v)?
409            }
410        }
411        Ok(())
412    }
413}
414
415//--- Debug
416
417impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Understood<DauVariant, Octs> {
418    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419        f.debug_tuple("Understood<DauVariant>")
420            .field(&format_args!("{}", self))
421            .finish()
422    }
423}
424
425impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Understood<DhuVariant, Octs> {
426    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
427        f.debug_tuple("Understood<DhuVariant>")
428            .field(&format_args!("{}", self))
429            .finish()
430    }
431}
432
433impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Understood<N3uVariant, Octs> {
434    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435        f.debug_tuple("Understood<N3uVariant>")
436            .field(&format_args!("{}", self))
437            .finish()
438    }
439}
440
441//--- Serialize
442
443#[cfg(feature = "serde")]
444impl<V, Octs: AsRef<[u8]>> serde::Serialize for Understood<V, Octs> {
445    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
446    where
447        S: serde::Serializer,
448    {
449        use serde::ser::SerializeSeq;
450        let mut list = serializer.serialize_seq(None)?;
451        for item in self.iter() {
452            list.serialize_element(&item)?;
453        }
454        list.end()
455    }
456}
457
458//--- Extended Opt and OptBuilder
459
460impl<Octs: Octets> Opt<Octs> {
461    /// Returns the first DAU option if present.
462    ///
463    /// This option lists the DNSSEC signature algorithms the requester
464    /// supports.
465    pub fn dau(&self) -> Option<Dau<Octs::Range<'_>>> {
466        self.first()
467    }
468
469    /// Returns the first DHU option if present.
470    ///
471    /// This option lists the DS hash algorithms the requester supports.
472    pub fn dhu(&self) -> Option<Dhu<Octs::Range<'_>>> {
473        self.first()
474    }
475
476    /// Returns the first N3U option if present.
477    ///
478    /// This option lists the NSEC3 hash algorithms the requester supports.
479    pub fn n3u(&self) -> Option<N3u<Octs::Range<'_>>> {
480        self.first()
481    }
482}
483
484impl<Target: Composer> OptBuilder<'_, Target> {
485    /// Appends a DAU option.
486    ///
487    /// The DAU option lists the DNSSEC signature algorithms the requester
488    /// supports.
489    pub fn dau(
490        &mut self,
491        algs: &impl AsRef<[SecurityAlgorithm]>,
492    ) -> Result<(), BuildDataError> {
493        Ok(self.push_raw_option(
494            OptionCode::DAU,
495            u16::try_from(
496                algs.as_ref().len()
497                    * usize::from(SecurityAlgorithm::COMPOSE_LEN),
498            )
499            .map_err(|_| BuildDataError::LongOptData)?,
500            |octs| {
501                algs.as_ref().iter().try_for_each(|item| item.compose(octs))
502            },
503        )?)
504    }
505
506    /// Appends a DHU option.
507    ///
508    /// The DHU option lists the DS hash algorithms the requester supports.
509    pub fn dhu(
510        &mut self,
511        algs: &impl AsRef<[SecurityAlgorithm]>,
512    ) -> Result<(), BuildDataError> {
513        Ok(self.push_raw_option(
514            OptionCode::DHU,
515            u16::try_from(
516                algs.as_ref().len()
517                    * usize::from(SecurityAlgorithm::COMPOSE_LEN),
518            )
519            .map_err(|_| BuildDataError::LongOptData)?,
520            |octs| {
521                algs.as_ref().iter().try_for_each(|item| item.compose(octs))
522            },
523        )?)
524    }
525
526    /// Appends a N3U option.
527    ///
528    /// The N3U option lists the NSEC3 hash algorithms the requester supports.
529    pub fn n3u(
530        &mut self,
531        algs: &impl AsRef<[SecurityAlgorithm]>,
532    ) -> Result<(), BuildDataError> {
533        Ok(self.push_raw_option(
534            OptionCode::N3U,
535            u16::try_from(
536                algs.as_ref().len()
537                    * usize::from(SecurityAlgorithm::COMPOSE_LEN),
538            )
539            .map_err(|_| BuildDataError::LongOptData)?,
540            |octs| {
541                algs.as_ref().iter().try_for_each(|item| item.compose(octs))
542            },
543        )?)
544    }
545}
546
547//------------ SecurityAlgorithmsIter ----------------------------------------
548
549pub struct SecurityAlgorithmIter<'a>(slice::Iter<'a, u8>);
550
551impl<'a> SecurityAlgorithmIter<'a> {
552    fn new(slice: &'a [u8]) -> Self {
553        SecurityAlgorithmIter(slice.iter())
554    }
555}
556
557impl Iterator for SecurityAlgorithmIter<'_> {
558    type Item = SecurityAlgorithm;
559
560    fn next(&mut self) -> Option<Self::Item> {
561        self.0.next().map(|x| SecurityAlgorithm::from_int(*x))
562    }
563}
564
565//============ Tests ========================================================
566
567#[cfg(test)]
568#[cfg(all(feature = "std", feature = "bytes"))]
569mod test {
570    use super::super::test::test_option_compose_parse;
571    use super::*;
572
573    #[test]
574    #[allow(clippy::redundant_closure)] // lifetimes ...
575    fn dau_compose_parse() {
576        test_option_compose_parse(
577            &Dau::from_octets("foof").unwrap(),
578            |parser| Dau::parse(parser),
579        );
580    }
581}