domain/base/opt/
mod.rs

1// XXX TODO: Easier access to individual options.
2// XXX TODO: Documentation and tests.
3//
4//! Record data for OPT records.
5//!
6//! Since DNS message headers are relatively short, the amount of information
7//! that can be conveyed through them is very limited. In order to provide an
8//! extensible means to transmit additional information, [RFC 6891] introduces
9//! a resource record called OPT that can be added to the additional section
10//! of a message. The record data in turn consists of a sequence of options.
11//!
12//! This module contains the types for working with both the OPT record and
13//! its record data. It defines types for each of the currently defined
14//! options. As with record data types in the [rdata] module, these are
15//! arranged in sub-modules according to the RFC that defined them and then
16//! re-exported here.
17//!
18//! [RFC 6891]: https://tools.ietf.org/html/rfc6891
19//! [rdata]: ../../rdata/index.html
20
21//============ Sub-modules and Re-exports ====================================
22//
23// All of these are in a macro. The macro also defines `AllOptData`.
24
25#[macro_use]
26mod macros;
27
28pub mod algsig;
29pub mod chain;
30pub mod cookie;
31pub mod expire;
32pub mod exterr;
33pub mod keepalive;
34pub mod keytag;
35pub mod nsid;
36pub mod padding;
37pub mod subnet;
38
39opt_types! {
40    algsig::{Dau<Octs>, Dhu<Octs>, N3u<Octs>};
41    chain::{Chain<Name>};
42    cookie::{Cookie};
43    expire::{Expire};
44    exterr::{ExtendedError<Octs>};
45    keepalive::{TcpKeepalive};
46    keytag::{KeyTag<Octs>};
47    nsid::{Nsid<Octs>};
48    padding::{Padding<Octs>};
49    subnet::{ClientSubnet};
50}
51
52//============ Module Content ================================================
53
54use super::cmp::CanonicalOrd;
55use super::header::Header;
56use super::iana::{Class, OptRcode, OptionCode, Rtype};
57use super::name::{Name, ToName};
58use super::rdata::{ComposeRecordData, ParseRecordData, RecordData};
59use super::record::{Record, Ttl};
60use super::wire::{Compose, Composer, FormError, ParseError};
61use super::zonefile_fmt::{self, Formatter, ZonefileFmt};
62use crate::utils::base16;
63use core::cmp::Ordering;
64use core::marker::PhantomData;
65use core::{fmt, hash, mem};
66use octseq::builder::{EmptyBuilder, OctetsBuilder, ShortBuf};
67use octseq::octets::{Octets, OctetsFrom};
68use octseq::parse::Parser;
69
70//------------ Opt -----------------------------------------------------------
71
72/// OPT record data.
73///
74/// This is the record data type for OPT records and can be used as the data
75/// type parameter in [`Record`]. It simply wraps an octets sequence with all
76/// the record data. It guarantees that the data contains a correctly
77/// formatted sequence of options but doesn’t guarantee that the options
78/// themselves are correct. You can iterate over options via the [`iter`]
79/// method.
80///
81/// Since some of the information of the OPT record is transmitted in the
82/// record header, a special type [`OptRecord`] exists, that contains all
83/// the OPT data which is the preferred way of accessing this data.
84///
85/// [`iter`]: #method.iter
86/// [`OptRecord`]: struct.OptRecord.html
87#[derive(Clone)]
88#[repr(transparent)]
89pub struct Opt<Octs: ?Sized> {
90    octets: Octs,
91}
92
93#[cfg(feature = "serde")]
94impl<O: AsRef<[u8]>> serde::Serialize for Opt<O> {
95    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
96    where
97        S: serde::Serializer,
98    {
99        use serde::ser::SerializeSeq;
100        let mut list = serializer.serialize_seq(None)?;
101
102        for rec in self.for_slice_ref().iter::<AllOptData<_, _>>() {
103            let Ok(rec) = rec else {
104                continue;
105            };
106            list.serialize_element(&rec)?;
107        }
108
109        list.end()
110    }
111}
112
113impl Opt<()> {
114    /// The rtype of this record data type.
115    pub(crate) const RTYPE: Rtype = Rtype::OPT;
116}
117
118impl<Octs: EmptyBuilder> Opt<Octs> {
119    /// Creates empty OPT record data.
120    pub fn empty() -> Self {
121        Self {
122            octets: Octs::empty(),
123        }
124    }
125}
126
127impl<Octs: AsRef<[u8]>> Opt<Octs> {
128    /// Creates OPT record data from an octets sequence.
129    ///
130    /// The function checks whether the octets contain a sequence of
131    /// options. It does not check whether the options themselves are valid.
132    pub fn from_octets(octets: Octs) -> Result<Self, ParseError> {
133        Opt::check_slice(octets.as_ref())?;
134        Ok(unsafe { Self::from_octets_unchecked(octets) })
135    }
136
137    /// Creates OPT record data from octets without checking.
138    ///
139    /// # Safety
140    ///
141    /// The caller needs to ensure that the slice contains correctly encoded
142    /// OPT record data. The data of the options themselves does not need to
143    /// be correct.
144    unsafe fn from_octets_unchecked(octets: Octs) -> Self {
145        Self { octets }
146    }
147
148    /// Parses OPT record data from the beginning of a parser.
149    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
150        parser: &mut Parser<'a, Src>,
151    ) -> Result<Self, ParseError> {
152        let len = parser.remaining();
153        Self::from_octets(parser.parse_octets(len)?)
154    }
155}
156
157impl Opt<[u8]> {
158    /// Creates OPT record data from an octets slice.
159    pub fn from_slice(slice: &[u8]) -> Result<&Self, ParseError> {
160        Self::check_slice(slice)?;
161        Ok(unsafe { Self::from_slice_unchecked(slice) })
162    }
163
164    /// Creates OPT record data from an octets slice without checking.
165    ///
166    /// # Safety
167    ///
168    /// The caller needs to ensure that the slice contains correctly encoded
169    /// OPT record data. The data of the options themselves does not need to
170    /// be correct.
171    unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
172        // SAFETY: Opt has repr(transparent)
173        mem::transmute(slice)
174    }
175
176    /// Checks that the slice contains acceptable OPT record data.
177    fn check_slice(slice: &[u8]) -> Result<(), ParseError> {
178        if slice.len() > usize::from(u16::MAX) {
179            return Err(FormError::new("long record data").into());
180        }
181        let mut parser = Parser::from_ref(slice);
182        while parser.remaining() > 0 {
183            parser.advance(2)?;
184            let len = parser.parse_u16_be()?;
185            parser.advance(len as usize)?;
186        }
187        Ok(())
188    }
189}
190
191impl<Octs: AsRef<[u8]> + ?Sized> Opt<Octs> {
192    pub fn for_slice_ref(&self) -> Opt<&[u8]> {
193        unsafe { Opt::from_octets_unchecked(self.octets.as_ref()) }
194    }
195}
196
197impl<Octs: AsRef<[u8]> + ?Sized> Opt<Octs> {
198    /// Returns the length of the OPT record data.
199    pub fn len(&self) -> usize {
200        self.octets.as_ref().len()
201    }
202
203    /// Returns whether the OPT record data is empty.
204    pub fn is_empty(&self) -> bool {
205        self.octets.as_ref().is_empty()
206    }
207
208    /// Returns an iterator over options of a given type.
209    ///
210    /// The returned iterator will return only options represented by type
211    /// `D` and quietly skip over all the others.
212    pub fn iter<'s, Data>(&'s self) -> OptIter<'s, Octs, Data>
213    where
214        Octs: Octets,
215        Data: ParseOptData<'s, Octs>,
216    {
217        OptIter::new(&self.octets)
218    }
219
220    /// Returns the first option of a given type if present.
221    ///
222    /// If trying to parse this first option fails, returns `None` as well.
223    pub fn first<'s, Data>(&'s self) -> Option<Data>
224    where
225        Octs: Octets,
226        Data: ParseOptData<'s, Octs>,
227    {
228        self.iter::<Data>().next()?.ok()
229    }
230}
231
232impl<Octs: Composer> Opt<Octs> {
233    /// Appends a new option to the OPT data.
234    pub fn push<Opt: ComposeOptData + ?Sized>(
235        &mut self,
236        option: &Opt,
237    ) -> Result<(), BuildDataError> {
238        self.push_raw_option(option.code(), option.compose_len(), |target| {
239            option.compose_option(target)
240        })
241    }
242
243    /// Appends a raw option to the OPT data.
244    ///
245    /// The method will append an option with the given option code. The data
246    /// of the option will be written via the closure `op`.
247    pub fn push_raw_option<F>(
248        &mut self,
249        code: OptionCode,
250        option_len: u16,
251        op: F,
252    ) -> Result<(), BuildDataError>
253    where
254        F: FnOnce(&mut Octs) -> Result<(), Octs::AppendError>,
255    {
256        LongOptData::check_len(
257            self.octets
258                .as_ref()
259                .len()
260                .saturating_add(usize::from(option_len)),
261        )?;
262
263        code.compose(&mut self.octets)?;
264        option_len.compose(&mut self.octets)?;
265        op(&mut self.octets)?;
266        Ok(())
267    }
268}
269
270//--- OctetsFrom
271
272impl<Octs, SrcOcts> OctetsFrom<Opt<SrcOcts>> for Opt<Octs>
273where
274    Octs: OctetsFrom<SrcOcts>,
275{
276    type Error = Octs::Error;
277
278    fn try_octets_from(source: Opt<SrcOcts>) -> Result<Self, Self::Error> {
279        Octs::try_octets_from(source.octets).map(|octets| Opt { octets })
280    }
281}
282
283//--- PartialEq and Eq
284
285impl<Octs, Other> PartialEq<Opt<Other>> for Opt<Octs>
286where
287    Octs: AsRef<[u8]> + ?Sized,
288    Other: AsRef<[u8]> + ?Sized,
289{
290    fn eq(&self, other: &Opt<Other>) -> bool {
291        self.octets.as_ref().eq(other.octets.as_ref())
292    }
293}
294
295impl<Octs: AsRef<[u8]> + ?Sized> Eq for Opt<Octs> {}
296
297//--- PartialOrd, Ord, and CanonicalOrd
298
299impl<Octs, Other> PartialOrd<Opt<Other>> for Opt<Octs>
300where
301    Octs: AsRef<[u8]> + ?Sized,
302    Other: AsRef<[u8]> + ?Sized,
303{
304    fn partial_cmp(&self, other: &Opt<Other>) -> Option<Ordering> {
305        self.octets.as_ref().partial_cmp(other.octets.as_ref())
306    }
307}
308
309impl<Octs: AsRef<[u8]> + ?Sized> Ord for Opt<Octs> {
310    fn cmp(&self, other: &Self) -> Ordering {
311        self.octets.as_ref().cmp(other.octets.as_ref())
312    }
313}
314
315impl<Octs, Other> CanonicalOrd<Opt<Other>> for Opt<Octs>
316where
317    Octs: AsRef<[u8]> + ?Sized,
318    Other: AsRef<[u8]> + ?Sized,
319{
320    fn canonical_cmp(&self, other: &Opt<Other>) -> Ordering {
321        self.octets.as_ref().cmp(other.octets.as_ref())
322    }
323}
324
325//--- Hash
326
327impl<Octs: AsRef<[u8]> + ?Sized> hash::Hash for Opt<Octs> {
328    fn hash<H: hash::Hasher>(&self, state: &mut H) {
329        self.octets.as_ref().hash(state)
330    }
331}
332
333//--- RecordData, ParseRecordData, and ComposeRecordData
334
335impl<Octs: ?Sized> RecordData for Opt<Octs> {
336    fn rtype(&self) -> Rtype {
337        Rtype::OPT
338    }
339}
340
341impl<'a, Octs> ParseRecordData<'a, Octs> for Opt<Octs::Range<'a>>
342where
343    Octs: Octets + ?Sized,
344{
345    fn parse_rdata(
346        rtype: Rtype,
347        parser: &mut Parser<'a, Octs>,
348    ) -> Result<Option<Self>, ParseError> {
349        if rtype == Rtype::OPT {
350            Self::parse(parser).map(Some)
351        } else {
352            Ok(None)
353        }
354    }
355}
356
357impl<Octs: AsRef<[u8]> + ?Sized> ComposeRecordData for Opt<Octs> {
358    fn rdlen(&self, _compress: bool) -> Option<u16> {
359        Some(u16::try_from(self.octets.as_ref().len()).expect("long OPT"))
360    }
361
362    fn compose_rdata<Target: Composer + ?Sized>(
363        &self,
364        target: &mut Target,
365    ) -> Result<(), Target::AppendError> {
366        target.append_slice(self.octets.as_ref())
367    }
368
369    fn compose_canonical_rdata<Target: Composer + ?Sized>(
370        &self,
371        target: &mut Target,
372    ) -> Result<(), Target::AppendError> {
373        self.compose_rdata(target)
374    }
375}
376
377//--- Display
378
379impl<Octs: AsRef<[u8]> + ?Sized> fmt::Display for Opt<Octs> {
380    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381        // XXX TODO Print this properly.
382        f.write_str("OPT ...")
383    }
384}
385
386impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Opt<Octs> {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        f.write_str("Opt(")?;
389        fmt::Display::fmt(self, f)?;
390        f.write_str(")")
391    }
392}
393
394//--- ZonefileFmt
395
396impl<Octs: AsRef<[u8]> + ?Sized> ZonefileFmt for Opt<Octs> {
397    fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
398        // XXX TODO Print this properly.
399        p.write_token("OPT ...")
400    }
401}
402
403//------------ OptHeader -----------------------------------------------------
404
405/// The header of an OPT record.
406///
407/// The OPT record reappropriates the record header for encoding some
408/// basic information. This type provides access to this information. It
409/// consists of the record header with the exception of the final `rdlen`
410/// field.
411///
412/// This is so that [`OptBuilder`] can safely deref to this type.
413///
414/// [`OptBuilder`]: crate::base::message_builder::OptBuilder
415//    +------------+--------------+------------------------------+
416//    | Field Name | Field Type   | Description                  |
417//    +------------+--------------+------------------------------+
418//    | NAME       | domain name  | MUST be 0 (root domain)      |
419//    | TYPE       | u_int16_t    | OPT (41)                     |
420//    | CLASS      | u_int16_t    | requestor's UDP payload size |
421//    | TTL        | u_int32_t    | extended RCODE and flags     |
422//    | RDLEN      | u_int16_t    | length of all RDATA          |
423//    | RDATA      | octet stream | {attribute,value} pairs      |
424//    +------------+--------------+------------------------------+
425#[derive(Copy, Clone, Debug, Eq, PartialEq)]
426#[repr(transparent)]
427pub struct OptHeader {
428    /// The bytes of the header.
429    inner: [u8; 9],
430}
431
432impl OptHeader {
433    /// Returns a reference to an OPT header pointing into a record’s octets.
434    #[must_use]
435    pub fn for_record_slice(slice: &[u8]) -> &OptHeader {
436        assert!(slice.len() >= mem::size_of::<Self>());
437
438        // SAFETY: the pointer cast is sound because
439        //   - OptHeader has repr(transparent) and
440        //   - the size of the slice is large enough
441        unsafe { &*(slice.as_ptr() as *const OptHeader) }
442    }
443
444    /// Returns a mutable reference pointing into a record’s octets.
445    pub fn for_record_slice_mut(slice: &mut [u8]) -> &mut OptHeader {
446        assert!(slice.len() >= mem::size_of::<Self>());
447
448        // SAFETY: the pointer cast is sound because
449        //   - OptHeader has repr(transparent) and
450        //   - the size of the slice is large enough
451        unsafe { &mut *(slice.as_mut_ptr() as *mut OptHeader) }
452    }
453
454    /// Returns the UDP payload size.
455    ///
456    /// Through this field a sender of a message can signal the maximum size
457    /// of UDP payload the sender is able to handle when receiving messages.
458    /// This value refers to the abilities of the sender’s DNS implementation,
459    /// not such things as network MTUs. Which means that the largest UDP
460    /// payload that can actually be sent back to the sender may be smaller.
461    #[must_use]
462    pub fn udp_payload_size(&self) -> u16 {
463        u16::from_be_bytes(self.inner[3..5].try_into().unwrap())
464    }
465
466    /// Sets the UDP payload size value.
467    pub fn set_udp_payload_size(&mut self, value: u16) {
468        self.inner[3..5].copy_from_slice(&value.to_be_bytes())
469    }
470
471    /// Returns the extended rcode.
472    ///
473    /// Some of the bits of the rcode are stored in the regular message
474    /// header. Such a header needs to be passed to the method.
475    #[must_use]
476    pub fn rcode(&self, header: Header) -> OptRcode {
477        OptRcode::from_parts(header.rcode(), self.inner[5])
478    }
479
480    /// Sets the extend rcode of the OPT header.
481    ///
482    /// This method _only_ sets the upper bits of the rcode. The lower bits
483    /// need to be set in the message header.
484    pub fn set_rcode(&mut self, rcode: OptRcode) {
485        self.inner[5] = rcode.ext()
486    }
487
488    /// Returns the EDNS version of the OPT header.
489    ///
490    /// Only EDNS version 0 is currently defined.
491    #[must_use]
492    pub fn version(&self) -> u8 {
493        self.inner[6]
494    }
495
496    /// Sets the EDNS version of the OPT header.
497    pub fn set_version(&mut self, version: u8) {
498        self.inner[6] = version
499    }
500
501    /// Returns the value of the DNSSEC OK (DO) bit.
502    ///
503    /// By setting this bit, a resolver indicates that it is interested in
504    /// also receiving the DNSSEC-related resource records necessary to
505    /// validate an answer. The bit and the related procedures are defined in
506    /// [RFC 3225].
507    ///
508    /// [RFC 3225]: https://tools.ietf.org/html/rfc3225
509    #[must_use]
510    pub fn dnssec_ok(&self) -> bool {
511        self.inner[7] & 0x80 != 0
512    }
513
514    /// Sets the DNSSEC OK (DO) bit to the given value.
515    pub fn set_dnssec_ok(&mut self, value: bool) {
516        if value {
517            self.inner[7] |= 0x80
518        } else {
519            self.inner[7] &= 0x7F
520        }
521    }
522
523    pub fn compose<Target: OctetsBuilder + ?Sized>(
524        self,
525        target: &mut Target,
526    ) -> Result<(), Target::AppendError> {
527        target.append_slice(&self.inner)
528    }
529}
530
531impl Default for OptHeader {
532    fn default() -> Self {
533        OptHeader {
534            inner: [0, 0, 41, 0, 0, 0, 0, 0, 0],
535        }
536    }
537}
538
539//------------ OptRecord -----------------------------------------------------
540
541/// An entire OPT record.
542///
543/// Because the EDNS specificiation uses parts of the header of the OPT record
544/// to convey some information, a special record type is necessary for OPT
545/// records. You can convert a normal record with [`Opt`] record data into
546/// an `OptRecord` via the [`from_record`][OptRecord::from_record] function.
547#[derive(Clone)]
548pub struct OptRecord<Octs> {
549    /// The UDP payload size field from the record header.
550    udp_payload_size: u16,
551
552    /// The extended rcode.
553    ext_rcode: u8,
554
555    /// The EDNS version.
556    version: u8,
557
558    /// The EDNS flags.
559    flags: u16,
560
561    /// The record data.
562    data: Opt<Octs>,
563}
564
565impl<Octs> OptRecord<Octs> {
566    /// Converts a regular record into an OPT record
567    pub fn from_record<N: ToName>(record: Record<N, Opt<Octs>>) -> Self {
568        OptRecord {
569            udp_payload_size: record.class().to_int(),
570            ext_rcode: (record.ttl().as_secs() >> 24) as u8,
571            version: (record.ttl().as_secs() >> 16) as u8,
572            flags: record.ttl().as_secs() as u16,
573            data: record.into_data(),
574        }
575    }
576
577    /// Converts the OPT record into a regular record.
578    pub fn as_record(&self) -> Record<&'static Name<[u8]>, Opt<&[u8]>>
579    where
580        Octs: AsRef<[u8]>,
581    {
582        Record::new(
583            Name::root_slice(),
584            Class::from_int(self.udp_payload_size),
585            Ttl::from_secs(
586                (u32::from(self.ext_rcode) << 24)
587                    | (u32::from(self.version) << 16)
588                    | u32::from(self.flags),
589            ),
590            self.data.for_slice_ref(),
591        )
592    }
593
594    /// Returns the UDP payload size.
595    ///
596    /// Through this field a sender of a message can signal the maximum size
597    /// of UDP payload the sender is able to handle when receiving messages.
598    /// This value refers to the abilities of the sender’s DNS implementation,
599    /// not such things as network MTUs. Which means that the largest UDP
600    /// payload that can actually be sent back to the sender may be smaller.
601    pub fn udp_payload_size(&self) -> u16 {
602        self.udp_payload_size
603    }
604
605    /// Sets the UDP payload size.
606    pub fn set_udp_payload_size(&mut self, value: u16) {
607        self.udp_payload_size = value
608    }
609
610    /// Returns the extended rcode.
611    ///
612    /// Some of the bits of the rcode are stored in the regular message
613    /// header. Such a header needs to be passed to the method.
614    pub fn rcode(&self, header: Header) -> OptRcode {
615        OptRcode::from_parts(header.rcode(), self.ext_rcode)
616    }
617
618    /// Returns the EDNS version of the OPT header.
619    ///
620    /// Only EDNS version 0 is currently defined.
621    pub fn version(&self) -> u8 {
622        self.version
623    }
624
625    /// Returns the value of the DNSSEC OK (DO) bit.
626    ///
627    /// By setting this bit, a resolver indicates that it is interested in
628    /// also receiving the DNSSEC-related resource records necessary to
629    /// validate an answer. The bit and the related procedures are defined in
630    /// [RFC 3225].
631    ///
632    /// [RFC 3225]: https://tools.ietf.org/html/rfc3225
633    pub fn dnssec_ok(&self) -> bool {
634        self.flags & 0x8000 != 0
635    }
636
637    pub fn set_dnssec_ok(&mut self, value: bool) {
638        if value {
639            self.flags |= 0x8000;
640        } else {
641            self.flags &= !0x8000;
642        }
643    }
644
645    /// Returns a reference to the raw options.
646    pub fn opt(&self) -> &Opt<Octs> {
647        &self.data
648    }
649}
650
651impl<Octs: Composer> OptRecord<Octs> {
652    /// Appends a new option to the OPT data.
653    pub fn push<Opt: ComposeOptData + ?Sized>(
654        &mut self,
655        option: &Opt,
656    ) -> Result<(), BuildDataError> {
657        self.data.push(option)
658    }
659
660    /// Appends a raw option to the OPT data.
661    ///
662    /// The method will append an option with the given option code. The data
663    /// of the option will be written via the closure `op`.
664    pub fn push_raw_option<F>(
665        &mut self,
666        code: OptionCode,
667        option_len: u16,
668        op: F,
669    ) -> Result<(), BuildDataError>
670    where
671        F: FnOnce(&mut Octs) -> Result<(), Octs::AppendError>,
672    {
673        self.data.push_raw_option(code, option_len, op)
674    }
675}
676
677impl<Octs: EmptyBuilder> Default for OptRecord<Octs> {
678    fn default() -> Self {
679        Self {
680            udp_payload_size: 0,
681            ext_rcode: 0,
682            version: 0,
683            flags: 0,
684            data: Opt::empty(),
685        }
686    }
687}
688
689//--- From
690
691impl<Octs, N: ToName> From<Record<N, Opt<Octs>>> for OptRecord<Octs> {
692    fn from(record: Record<N, Opt<Octs>>) -> Self {
693        Self::from_record(record)
694    }
695}
696
697//--- OctetsFrom
698
699impl<Octs, SrcOcts> OctetsFrom<OptRecord<SrcOcts>> for OptRecord<Octs>
700where
701    Octs: OctetsFrom<SrcOcts>,
702{
703    type Error = Octs::Error;
704
705    fn try_octets_from(
706        source: OptRecord<SrcOcts>,
707    ) -> Result<Self, Self::Error> {
708        Ok(OptRecord {
709            udp_payload_size: source.udp_payload_size,
710            ext_rcode: source.ext_rcode,
711            version: source.version,
712            flags: source.flags,
713            data: Opt::try_octets_from(source.data)?,
714        })
715    }
716}
717
718//--- AsRef
719
720impl<Octs> AsRef<Opt<Octs>> for OptRecord<Octs> {
721    fn as_ref(&self) -> &Opt<Octs> {
722        &self.data
723    }
724}
725
726//--- Debug
727
728impl<Octs: AsRef<[u8]>> fmt::Debug for OptRecord<Octs> {
729    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
730        f.debug_struct("OptRecord")
731            .field("udp_payload_size", &self.udp_payload_size)
732            .field("ext_rcord", &self.ext_rcode)
733            .field("version", &self.version)
734            .field("flags", &self.flags)
735            .field("data", &self.data)
736            .finish()
737    }
738}
739
740//------------ OptionHeader --------------------------------------------------
741
742/// The header of an OPT option.
743///
744/// This header contains a 16 bit option code identifying the kind of option
745/// we are dealing with and a 16 bit length describing the lenngth in octets
746/// of the option data.
747#[derive(Clone, Copy, Debug)]
748pub struct OptionHeader {
749    /// The option code.
750    code: u16,
751
752    /// The length of the option’s data in octets.
753    len: u16,
754}
755
756#[allow(clippy::len_without_is_empty)]
757impl OptionHeader {
758    /// Creates a new option header from code and length.
759    #[must_use]
760    pub fn new(code: u16, len: u16) -> Self {
761        OptionHeader { code, len }
762    }
763
764    /// Returns the option code.
765    #[must_use]
766    pub fn code(self) -> u16 {
767        self.code
768    }
769
770    /// Returns the length of the option data.
771    #[must_use]
772    pub fn len(self) -> u16 {
773        self.len
774    }
775
776    pub fn parse<Octs: AsRef<[u8]> + ?Sized>(
777        parser: &mut Parser<'_, Octs>,
778    ) -> Result<Self, ParseError> {
779        Ok(OptionHeader::new(
780            parser.parse_u16_be()?,
781            parser.parse_u16_be()?,
782        ))
783    }
784}
785
786//------------ OptIter -------------------------------------------------------
787
788/// An iterator over the options of an OPT record.
789///
790/// The iterator is generic over a specific option type. It skips over all
791/// options that this type does not want to parse. It returns a result that
792/// is either a parsed option or a parse error. These errors are only for the
793/// particular option. After such an error you can continue to iterate until
794/// `None` indicates that you’ve reached the end of the record.
795#[derive(Clone, Debug)]
796pub struct OptIter<'a, Octs: ?Sized, D> {
797    /// A parser for the OPT record data.
798    parser: Parser<'a, Octs>,
799
800    /// The marker to remember which record data we use.
801    marker: PhantomData<D>,
802}
803
804impl<'a, Octs, D> OptIter<'a, Octs, D>
805where
806    Octs: Octets + ?Sized,
807    D: ParseOptData<'a, Octs>,
808{
809    /// Creates an iterator from a reference to the OPT record data.
810    fn new(octets: &'a Octs) -> Self {
811        OptIter {
812            parser: Parser::from_ref(octets),
813            marker: PhantomData,
814        }
815    }
816
817    /// Returns the next item from the parser.
818    ///
819    /// Expects there to be another option available and will return a
820    /// parse error otherwise. Return `Ok(None)` if the option type didn’t
821    /// want to parse this option.
822    fn next_step(&mut self) -> Result<Option<D>, ParseError> {
823        let code = self.parser.parse_u16_be()?.into();
824        let len = self.parser.parse_u16_be()? as usize;
825        let mut parser = self.parser.parse_parser(len)?;
826        let res = D::parse_option(code, &mut parser)?;
827        if res.is_some() && parser.remaining() > 0 {
828            return Err(ParseError::Form(FormError::new(
829                "trailing data in option",
830            )));
831        }
832        Ok(res)
833    }
834}
835
836impl<'a, Octs, Data> Iterator for OptIter<'a, Octs, Data>
837where
838    Octs: Octets + ?Sized,
839    Data: ParseOptData<'a, Octs>,
840{
841    type Item = Result<Data, ParseError>;
842
843    fn next(&mut self) -> Option<Self::Item> {
844        while self.parser.remaining() > 0 {
845            match self.next_step() {
846                Ok(Some(res)) => return Some(Ok(res)),
847                Ok(None) => {}
848                Err(err) => {
849                    // Advance to end so we’ll return None from now on.
850                    self.parser.advance_to_end();
851                    return Some(Err(err));
852                }
853            }
854        }
855        None
856    }
857}
858
859//------------ OptData -------------------------------------------------------
860
861/// A type representing an OPT option.
862///
863/// The type needs to be able to report the option code to use for the
864/// encoding via the [`code`][Self::code] method.
865pub trait OptData {
866    /// Returns the option code associated with this option.
867    fn code(&self) -> OptionCode;
868}
869
870//------------ ParseOptData --------------------------------------------------
871
872/// An OPT option that can be parsed from the record data.
873pub trait ParseOptData<'a, Octs: ?Sized>: OptData + Sized {
874    /// Parses the option code data.
875    ///
876    /// The data is for an option of `code`. The function may decide whether
877    /// it wants to parse data for that type. It should return `Ok(None)` if
878    /// it doesn’t.
879    ///
880    /// The `parser` is positioned at the beginning of the option data and is
881    /// is limited to the length of the data. The method only needs to parse
882    /// as much data as it needs. The caller has to make sure to deal with
883    /// data remaining in the parser.
884    ///
885    /// If the function doesn’t want to process the data, it must not touch
886    /// the parser. In particual, it must not advance it.
887    fn parse_option(
888        code: OptionCode,
889        parser: &mut Parser<'a, Octs>,
890    ) -> Result<Option<Self>, ParseError>;
891}
892
893//------------ ComposeOptData ------------------------------------------------
894
895/// An OPT option that can be written to wire format.
896pub trait ComposeOptData: OptData {
897    fn compose_len(&self) -> u16;
898
899    fn compose_option<Target: OctetsBuilder + ?Sized>(
900        &self,
901        target: &mut Target,
902    ) -> Result<(), Target::AppendError>;
903}
904
905//------------ UnknownOptData ------------------------------------------------
906
907/// An OPT option in its raw form.
908///
909/// This type accepts any option type via its option code and raw data.
910#[derive(Clone)]
911#[cfg_attr(feature = "serde", derive(serde::Serialize))]
912pub struct UnknownOptData<Octs> {
913    /// The option code for the option.
914    code: OptionCode,
915
916    /// The raw option data.
917    #[cfg_attr(
918        feature = "serde",
919        serde(
920            serialize_with = "crate::utils::base16::serde::serialize",
921            bound(
922                serialize = "Octs: AsRef<[u8]> + octseq::serde::SerializeOctets",
923            )
924        )
925    )]
926    data: Octs,
927}
928
929impl<Octs> UnknownOptData<Octs> {
930    /// Creates a new option from the code and data.
931    ///
932    /// The function returns an error if `data` is longer than 65,535 octets.
933    pub fn new(code: OptionCode, data: Octs) -> Result<Self, LongOptData>
934    where
935        Octs: AsRef<[u8]>,
936    {
937        LongOptData::check_len(data.as_ref().len())?;
938        Ok(unsafe { Self::new_unchecked(code, data) })
939    }
940
941    /// Creates a new option data value without checking.
942    ///
943    /// # Safety
944    ///
945    /// The caller needs to make sure that `data` is not longer than 65,535
946    /// octets.
947    pub unsafe fn new_unchecked(code: OptionCode, data: Octs) -> Self {
948        Self { code, data }
949    }
950
951    /// Returns the option code of the option.
952    pub fn code(&self) -> OptionCode {
953        self.code
954    }
955
956    /// Returns a reference for to the option data.
957    pub fn data(&self) -> &Octs {
958        &self.data
959    }
960
961    /// Returns a slice of the option data.
962    pub fn as_slice(&self) -> &[u8]
963    where
964        Octs: AsRef<[u8]>,
965    {
966        self.data.as_ref()
967    }
968
969    /// Returns a mutable slice of the option data.
970    pub fn as_slice_mut(&mut self) -> &mut [u8]
971    where
972        Octs: AsMut<[u8]>,
973    {
974        self.data.as_mut()
975    }
976}
977
978//--- OctetsFrom
979
980impl<Octs, SrcOcts> OctetsFrom<UnknownOptData<SrcOcts>>
981    for UnknownOptData<Octs>
982where
983    Octs: OctetsFrom<SrcOcts>,
984{
985    type Error = Octs::Error;
986
987    fn try_octets_from(
988        src: UnknownOptData<SrcOcts>,
989    ) -> Result<Self, Self::Error> {
990        Ok(unsafe {
991            Self::new_unchecked(src.code, Octs::try_octets_from(src.data)?)
992        })
993    }
994}
995//--- AsRef and AsMut
996
997impl<Octs> AsRef<Octs> for UnknownOptData<Octs> {
998    fn as_ref(&self) -> &Octs {
999        self.data()
1000    }
1001}
1002
1003impl<Octs: AsRef<[u8]>> AsRef<[u8]> for UnknownOptData<Octs> {
1004    fn as_ref(&self) -> &[u8] {
1005        self.as_slice()
1006    }
1007}
1008
1009impl<Octs: AsMut<[u8]>> AsMut<[u8]> for UnknownOptData<Octs> {
1010    fn as_mut(&mut self) -> &mut [u8] {
1011        self.as_slice_mut()
1012    }
1013}
1014
1015//--- OptData etc.
1016
1017impl<Octs: AsRef<[u8]>> OptData for UnknownOptData<Octs> {
1018    fn code(&self) -> OptionCode {
1019        self.code
1020    }
1021}
1022
1023impl<'a, Octs> ParseOptData<'a, Octs> for UnknownOptData<Octs::Range<'a>>
1024where
1025    Octs: Octets + ?Sized,
1026{
1027    fn parse_option(
1028        code: OptionCode,
1029        parser: &mut Parser<'a, Octs>,
1030    ) -> Result<Option<Self>, ParseError> {
1031        Self::new(code, parser.parse_octets(parser.remaining())?)
1032            .map(Some)
1033            .map_err(Into::into)
1034    }
1035}
1036
1037impl<Octs: AsRef<[u8]>> ComposeOptData for UnknownOptData<Octs> {
1038    fn compose_len(&self) -> u16 {
1039        self.data
1040            .as_ref()
1041            .len()
1042            .try_into()
1043            .expect("long option data")
1044    }
1045
1046    fn compose_option<Target: OctetsBuilder + ?Sized>(
1047        &self,
1048        target: &mut Target,
1049    ) -> Result<(), Target::AppendError> {
1050        target.append_slice(self.data.as_ref())
1051    }
1052}
1053
1054//--- Display and Debug
1055
1056impl<Octs: AsRef<[u8]>> fmt::Display for UnknownOptData<Octs> {
1057    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1058        base16::display(self.data.as_ref(), f)
1059    }
1060}
1061
1062impl<Octs: AsRef<[u8]>> fmt::Debug for UnknownOptData<Octs> {
1063    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1064        f.debug_struct("UnknownOptData")
1065            .field("code", &self.code)
1066            .field("data", &format_args!("{}", self))
1067            .finish()
1068    }
1069}
1070
1071//============ Error Types ===================================================
1072
1073//------------ LongOptData ---------------------------------------------------
1074
1075/// The octets sequence to be used for option data is too long.
1076#[derive(Clone, Copy, Debug)]
1077pub struct LongOptData(());
1078
1079impl LongOptData {
1080    #[must_use]
1081    pub fn as_str(self) -> &'static str {
1082        "option data too long"
1083    }
1084
1085    pub fn check_len(len: usize) -> Result<(), Self> {
1086        if len > usize::from(u16::MAX) {
1087            Err(Self(()))
1088        } else {
1089            Ok(())
1090        }
1091    }
1092}
1093
1094impl From<LongOptData> for ParseError {
1095    fn from(src: LongOptData) -> Self {
1096        ParseError::form_error(src.as_str())
1097    }
1098}
1099
1100impl fmt::Display for LongOptData {
1101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1102        f.write_str(self.as_str())
1103    }
1104}
1105
1106#[cfg(feature = "std")]
1107impl std::error::Error for LongOptData {}
1108
1109//------------ BuildDataError ------------------------------------------------
1110
1111/// An error happened while constructing an SVCB value.
1112#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1113pub enum BuildDataError {
1114    /// The value would exceed the allowed length of a value.
1115    LongOptData,
1116
1117    /// The underlying octets builder ran out of buffer space.
1118    ShortBuf,
1119}
1120
1121impl BuildDataError {
1122    /// Converts the error into a [`LongOptData`] error for ‘endless’ buffers.
1123    ///
1124    /// # Panics
1125    ///
1126    /// This method will panic if the error is of the `ShortBuf` variant.
1127    pub fn unlimited_buf(self) -> LongOptData {
1128        match self {
1129            Self::LongOptData => LongOptData(()),
1130            Self::ShortBuf => panic!("ShortBuf on unlimited buffer"),
1131        }
1132    }
1133}
1134
1135impl From<LongOptData> for BuildDataError {
1136    fn from(_: LongOptData) -> Self {
1137        Self::LongOptData
1138    }
1139}
1140
1141impl<T: Into<ShortBuf>> From<T> for BuildDataError {
1142    fn from(_: T) -> Self {
1143        Self::ShortBuf
1144    }
1145}
1146
1147//--- Display and Error
1148
1149impl fmt::Display for BuildDataError {
1150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1151        match self {
1152            Self::LongOptData => f.write_str("long option data"),
1153            Self::ShortBuf => ShortBuf.fmt(f),
1154        }
1155    }
1156}
1157
1158#[cfg(feature = "std")]
1159impl std::error::Error for BuildDataError {}
1160
1161//============ Tests =========================================================
1162
1163#[cfg(test)]
1164#[cfg(all(feature = "std", feature = "bytes"))]
1165pub(super) mod test {
1166    use super::*;
1167    use crate::base::rdata::test::{test_compose_parse, test_rdlen};
1168    use crate::base::record::ParsedRecord;
1169    use crate::base::{opt, MessageBuilder};
1170    use bytes::{Bytes, BytesMut};
1171    use core::fmt::Debug;
1172    use octseq::builder::infallible;
1173    use std::vec::Vec;
1174
1175    #[test]
1176    #[allow(clippy::redundant_closure)] // lifetimes ...
1177    fn opt_compose_parse_scan() {
1178        let rdata = Opt::from_octets("fo\x00\x03foo").unwrap();
1179        test_rdlen(&rdata);
1180        test_compose_parse(&rdata, |parser| Opt::parse(parser));
1181    }
1182
1183    #[test]
1184    fn opt_record_header() {
1185        let mut header = OptHeader::default();
1186        header.set_udp_payload_size(0x1234);
1187        header.set_rcode(OptRcode::BADVERS);
1188        header.set_version(0xbd);
1189        header.set_dnssec_ok(true);
1190        let mut buf = Vec::with_capacity(11);
1191        infallible(header.compose(&mut buf));
1192        infallible(0u16.compose(&mut buf));
1193        let mut buf = Parser::from_ref(buf.as_slice());
1194        let record = ParsedRecord::parse(&mut buf)
1195            .unwrap()
1196            .into_record::<Opt<_>>()
1197            .unwrap()
1198            .unwrap();
1199        let record = OptRecord::from_record(record);
1200        assert_eq!(record.udp_payload_size(), 0x1234);
1201        assert_eq!(record.ext_rcode, OptRcode::BADVERS.ext());
1202        assert_eq!(record.version(), 0xbd);
1203        assert!(record.dnssec_ok());
1204    }
1205
1206    #[test]
1207    fn opt_iter() {
1208        use self::opt::cookie::{ClientCookie, Cookie};
1209
1210        // Push two options and check that both are parseable
1211        let nsid = opt::Nsid::from_octets(&b"example"[..]).unwrap();
1212        let cookie = Cookie::new(
1213            ClientCookie::from_octets(1234u64.to_be_bytes()),
1214            None,
1215        );
1216        let msg = {
1217            let mut mb = MessageBuilder::new_vec().additional();
1218            mb.opt(|mb| {
1219                mb.push(&nsid)?;
1220                mb.push(&cookie)?;
1221                Ok(())
1222            })
1223            .unwrap();
1224            mb.into_message()
1225        };
1226
1227        // Parse both into specialized types
1228        let opt = msg.opt().unwrap();
1229        assert_eq!(Some(Ok(nsid)), opt.opt().iter::<opt::Nsid<_>>().next());
1230        assert_eq!(Some(Ok(cookie)), opt.opt().iter::<opt::Cookie>().next());
1231    }
1232
1233    pub fn test_option_compose_parse<In, F, Out>(data: &In, parse: F)
1234    where
1235        In: ComposeOptData + PartialEq<Out> + Debug,
1236        F: FnOnce(&mut Parser<'_, Bytes>) -> Result<Out, ParseError>,
1237        Out: Debug,
1238    {
1239        let mut buf = BytesMut::new();
1240        infallible(data.compose_option(&mut buf));
1241        let buf = buf.freeze();
1242        assert_eq!(buf.len(), usize::from(data.compose_len()));
1243        let mut parser = Parser::from_ref(&buf);
1244        let parsed = (parse)(&mut parser).unwrap();
1245        assert_eq!(parser.remaining(), 0);
1246        assert_eq!(*data, parsed);
1247    }
1248}