1#[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
52use 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#[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 pub(crate) const RTYPE: Rtype = Rtype::OPT;
116}
117
118impl<Octs: EmptyBuilder> Opt<Octs> {
119 pub fn empty() -> Self {
121 Self {
122 octets: Octs::empty(),
123 }
124 }
125}
126
127impl<Octs: AsRef<[u8]>> Opt<Octs> {
128 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 unsafe fn from_octets_unchecked(octets: Octs) -> Self {
145 Self { octets }
146 }
147
148 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 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 unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
172 mem::transmute(slice)
174 }
175
176 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 pub fn len(&self) -> usize {
200 self.octets.as_ref().len()
201 }
202
203 pub fn is_empty(&self) -> bool {
205 self.octets.as_ref().is_empty()
206 }
207
208 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 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 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 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
270impl<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
283impl<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
297impl<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
325impl<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
333impl<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
377impl<Octs: AsRef<[u8]> + ?Sized> fmt::Display for Opt<Octs> {
380 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381 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
394impl<Octs: AsRef<[u8]> + ?Sized> ZonefileFmt for Opt<Octs> {
397 fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
398 p.write_token("OPT ...")
400 }
401}
402
403#[derive(Copy, Clone, Debug, Eq, PartialEq)]
426#[repr(transparent)]
427pub struct OptHeader {
428 inner: [u8; 9],
430}
431
432impl OptHeader {
433 #[must_use]
435 pub fn for_record_slice(slice: &[u8]) -> &OptHeader {
436 assert!(slice.len() >= mem::size_of::<Self>());
437
438 unsafe { &*(slice.as_ptr() as *const OptHeader) }
442 }
443
444 pub fn for_record_slice_mut(slice: &mut [u8]) -> &mut OptHeader {
446 assert!(slice.len() >= mem::size_of::<Self>());
447
448 unsafe { &mut *(slice.as_mut_ptr() as *mut OptHeader) }
452 }
453
454 #[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 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 #[must_use]
476 pub fn rcode(&self, header: Header) -> OptRcode {
477 OptRcode::from_parts(header.rcode(), self.inner[5])
478 }
479
480 pub fn set_rcode(&mut self, rcode: OptRcode) {
485 self.inner[5] = rcode.ext()
486 }
487
488 #[must_use]
492 pub fn version(&self) -> u8 {
493 self.inner[6]
494 }
495
496 pub fn set_version(&mut self, version: u8) {
498 self.inner[6] = version
499 }
500
501 #[must_use]
510 pub fn dnssec_ok(&self) -> bool {
511 self.inner[7] & 0x80 != 0
512 }
513
514 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#[derive(Clone)]
548pub struct OptRecord<Octs> {
549 udp_payload_size: u16,
551
552 ext_rcode: u8,
554
555 version: u8,
557
558 flags: u16,
560
561 data: Opt<Octs>,
563}
564
565impl<Octs> OptRecord<Octs> {
566 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 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 pub fn udp_payload_size(&self) -> u16 {
602 self.udp_payload_size
603 }
604
605 pub fn set_udp_payload_size(&mut self, value: u16) {
607 self.udp_payload_size = value
608 }
609
610 pub fn rcode(&self, header: Header) -> OptRcode {
615 OptRcode::from_parts(header.rcode(), self.ext_rcode)
616 }
617
618 pub fn version(&self) -> u8 {
622 self.version
623 }
624
625 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 pub fn opt(&self) -> &Opt<Octs> {
647 &self.data
648 }
649}
650
651impl<Octs: Composer> OptRecord<Octs> {
652 pub fn push<Opt: ComposeOptData + ?Sized>(
654 &mut self,
655 option: &Opt,
656 ) -> Result<(), BuildDataError> {
657 self.data.push(option)
658 }
659
660 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
689impl<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
697impl<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
718impl<Octs> AsRef<Opt<Octs>> for OptRecord<Octs> {
721 fn as_ref(&self) -> &Opt<Octs> {
722 &self.data
723 }
724}
725
726impl<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#[derive(Clone, Copy, Debug)]
748pub struct OptionHeader {
749 code: u16,
751
752 len: u16,
754}
755
756#[allow(clippy::len_without_is_empty)]
757impl OptionHeader {
758 #[must_use]
760 pub fn new(code: u16, len: u16) -> Self {
761 OptionHeader { code, len }
762 }
763
764 #[must_use]
766 pub fn code(self) -> u16 {
767 self.code
768 }
769
770 #[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#[derive(Clone, Debug)]
796pub struct OptIter<'a, Octs: ?Sized, D> {
797 parser: Parser<'a, Octs>,
799
800 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 fn new(octets: &'a Octs) -> Self {
811 OptIter {
812 parser: Parser::from_ref(octets),
813 marker: PhantomData,
814 }
815 }
816
817 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 self.parser.advance_to_end();
851 return Some(Err(err));
852 }
853 }
854 }
855 None
856 }
857}
858
859pub trait OptData {
866 fn code(&self) -> OptionCode;
868}
869
870pub trait ParseOptData<'a, Octs: ?Sized>: OptData + Sized {
874 fn parse_option(
888 code: OptionCode,
889 parser: &mut Parser<'a, Octs>,
890 ) -> Result<Option<Self>, ParseError>;
891}
892
893pub 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#[derive(Clone)]
911#[cfg_attr(feature = "serde", derive(serde::Serialize))]
912pub struct UnknownOptData<Octs> {
913 code: OptionCode,
915
916 #[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 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 pub unsafe fn new_unchecked(code: OptionCode, data: Octs) -> Self {
948 Self { code, data }
949 }
950
951 pub fn code(&self) -> OptionCode {
953 self.code
954 }
955
956 pub fn data(&self) -> &Octs {
958 &self.data
959 }
960
961 pub fn as_slice(&self) -> &[u8]
963 where
964 Octs: AsRef<[u8]>,
965 {
966 self.data.as_ref()
967 }
968
969 pub fn as_slice_mut(&mut self) -> &mut [u8]
971 where
972 Octs: AsMut<[u8]>,
973 {
974 self.data.as_mut()
975 }
976}
977
978impl<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}
995impl<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
1015impl<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
1054impl<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#[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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1113pub enum BuildDataError {
1114 LongOptData,
1116
1117 ShortBuf,
1119}
1120
1121impl BuildDataError {
1122 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
1147impl 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#[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)] 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 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 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}