1#[macro_use]
26mod macros;
27opt_types! {
28 algsig::{Dau<Octs>, Dhu<Octs>, N3u<Octs>};
29 chain::{Chain<Name>};
30 cookie::{Cookie};
31 expire::{Expire};
32 exterr::{ExtendedError<Octs>};
33 keepalive::{TcpKeepalive};
34 keytag::{KeyTag<Octs>};
35 nsid::{Nsid<Octs>};
36 padding::{Padding<Octs>};
37 subnet::{ClientSubnet};
38}
39
40use super::cmp::CanonicalOrd;
43use super::header::Header;
44use super::iana::{Class, OptRcode, OptionCode, Rtype};
45use super::name::{Name, ToName};
46use super::rdata::{ComposeRecordData, ParseRecordData, RecordData};
47use super::record::{Record, Ttl};
48use super::wire::{Compose, Composer, FormError, ParseError};
49use crate::utils::base16;
50use core::cmp::Ordering;
51use core::marker::PhantomData;
52use core::{fmt, hash, mem};
53use octseq::builder::{EmptyBuilder, OctetsBuilder, ShortBuf};
54use octseq::octets::{Octets, OctetsFrom};
55use octseq::parse::Parser;
56
57#[derive(Clone)]
75#[repr(transparent)]
76pub struct Opt<Octs: ?Sized> {
77 octets: Octs,
78}
79
80#[cfg(feature = "serde")]
81impl<O: AsRef<[u8]>> serde::Serialize for Opt<O> {
82 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
83 where
84 S: serde::Serializer,
85 {
86 use serde::ser::SerializeSeq;
87 let mut list = serializer.serialize_seq(None)?;
88
89 for rec in self.for_slice_ref().iter::<AllOptData<_, _>>() {
90 let Ok(rec) = rec else {
91 continue;
92 };
93 list.serialize_element(&rec)?;
94 }
95
96 list.end()
97 }
98}
99
100impl Opt<()> {
101 pub(crate) const RTYPE: Rtype = Rtype::OPT;
103}
104
105impl<Octs: EmptyBuilder> Opt<Octs> {
106 pub fn empty() -> Self {
108 Self {
109 octets: Octs::empty(),
110 }
111 }
112}
113
114impl<Octs: AsRef<[u8]>> Opt<Octs> {
115 pub fn from_octets(octets: Octs) -> Result<Self, ParseError> {
120 Opt::check_slice(octets.as_ref())?;
121 Ok(unsafe { Self::from_octets_unchecked(octets) })
122 }
123
124 unsafe fn from_octets_unchecked(octets: Octs) -> Self {
132 Self { octets }
133 }
134
135 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
137 parser: &mut Parser<'a, Src>,
138 ) -> Result<Self, ParseError> {
139 let len = parser.remaining();
140 Self::from_octets(parser.parse_octets(len)?)
141 }
142}
143
144impl Opt<[u8]> {
145 pub fn from_slice(slice: &[u8]) -> Result<&Self, ParseError> {
147 Self::check_slice(slice)?;
148 Ok(unsafe { Self::from_slice_unchecked(slice) })
149 }
150
151 unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
159 mem::transmute(slice)
161 }
162
163 fn check_slice(slice: &[u8]) -> Result<(), ParseError> {
165 if slice.len() > usize::from(u16::MAX) {
166 return Err(FormError::new("long record data").into());
167 }
168 let mut parser = Parser::from_ref(slice);
169 while parser.remaining() > 0 {
170 parser.advance(2)?;
171 let len = parser.parse_u16_be()?;
172 parser.advance(len as usize)?;
173 }
174 Ok(())
175 }
176}
177
178impl<Octs: AsRef<[u8]> + ?Sized> Opt<Octs> {
179 pub fn for_slice_ref(&self) -> Opt<&[u8]> {
180 unsafe { Opt::from_octets_unchecked(self.octets.as_ref()) }
181 }
182}
183
184impl<Octs: AsRef<[u8]> + ?Sized> Opt<Octs> {
185 pub fn len(&self) -> usize {
187 self.octets.as_ref().len()
188 }
189
190 pub fn is_empty(&self) -> bool {
192 self.octets.as_ref().is_empty()
193 }
194
195 pub fn iter<'s, Data>(&'s self) -> OptIter<'s, Octs, Data>
200 where
201 Octs: Octets,
202 Data: ParseOptData<'s, Octs>,
203 {
204 OptIter::new(&self.octets)
205 }
206
207 pub fn first<'s, Data>(&'s self) -> Option<Data>
211 where
212 Octs: Octets,
213 Data: ParseOptData<'s, Octs>,
214 {
215 self.iter::<Data>().next()?.ok()
216 }
217}
218
219impl<Octs: Composer> Opt<Octs> {
220 pub fn push<Opt: ComposeOptData + ?Sized>(
222 &mut self,
223 option: &Opt,
224 ) -> Result<(), BuildDataError> {
225 self.push_raw_option(option.code(), option.compose_len(), |target| {
226 option.compose_option(target)
227 })
228 }
229
230 pub fn push_raw_option<F>(
235 &mut self,
236 code: OptionCode,
237 option_len: u16,
238 op: F,
239 ) -> Result<(), BuildDataError>
240 where
241 F: FnOnce(&mut Octs) -> Result<(), Octs::AppendError>,
242 {
243 LongOptData::check_len(
244 self.octets
245 .as_ref()
246 .len()
247 .saturating_add(usize::from(option_len)),
248 )?;
249
250 code.compose(&mut self.octets)?;
251 option_len.compose(&mut self.octets)?;
252 op(&mut self.octets)?;
253 Ok(())
254 }
255}
256
257impl<Octs, SrcOcts> OctetsFrom<Opt<SrcOcts>> for Opt<Octs>
260where
261 Octs: OctetsFrom<SrcOcts>,
262{
263 type Error = Octs::Error;
264
265 fn try_octets_from(source: Opt<SrcOcts>) -> Result<Self, Self::Error> {
266 Octs::try_octets_from(source.octets).map(|octets| Opt { octets })
267 }
268}
269
270impl<Octs, Other> PartialEq<Opt<Other>> for Opt<Octs>
273where
274 Octs: AsRef<[u8]> + ?Sized,
275 Other: AsRef<[u8]> + ?Sized,
276{
277 fn eq(&self, other: &Opt<Other>) -> bool {
278 self.octets.as_ref().eq(other.octets.as_ref())
279 }
280}
281
282impl<Octs: AsRef<[u8]> + ?Sized> Eq for Opt<Octs> {}
283
284impl<Octs, Other> PartialOrd<Opt<Other>> for Opt<Octs>
287where
288 Octs: AsRef<[u8]> + ?Sized,
289 Other: AsRef<[u8]> + ?Sized,
290{
291 fn partial_cmp(&self, other: &Opt<Other>) -> Option<Ordering> {
292 self.octets.as_ref().partial_cmp(other.octets.as_ref())
293 }
294}
295
296impl<Octs: AsRef<[u8]> + ?Sized> Ord for Opt<Octs> {
297 fn cmp(&self, other: &Self) -> Ordering {
298 self.octets.as_ref().cmp(other.octets.as_ref())
299 }
300}
301
302impl<Octs, Other> CanonicalOrd<Opt<Other>> for Opt<Octs>
303where
304 Octs: AsRef<[u8]> + ?Sized,
305 Other: AsRef<[u8]> + ?Sized,
306{
307 fn canonical_cmp(&self, other: &Opt<Other>) -> Ordering {
308 self.octets.as_ref().cmp(other.octets.as_ref())
309 }
310}
311
312impl<Octs: AsRef<[u8]> + ?Sized> hash::Hash for Opt<Octs> {
315 fn hash<H: hash::Hasher>(&self, state: &mut H) {
316 self.octets.as_ref().hash(state)
317 }
318}
319
320impl<Octs: ?Sized> RecordData for Opt<Octs> {
323 fn rtype(&self) -> Rtype {
324 Rtype::OPT
325 }
326}
327
328impl<'a, Octs> ParseRecordData<'a, Octs> for Opt<Octs::Range<'a>>
329where
330 Octs: Octets + ?Sized,
331{
332 fn parse_rdata(
333 rtype: Rtype,
334 parser: &mut Parser<'a, Octs>,
335 ) -> Result<Option<Self>, ParseError> {
336 if rtype == Rtype::OPT {
337 Self::parse(parser).map(Some)
338 } else {
339 Ok(None)
340 }
341 }
342}
343
344impl<Octs: AsRef<[u8]> + ?Sized> ComposeRecordData for Opt<Octs> {
345 fn rdlen(&self, _compress: bool) -> Option<u16> {
346 Some(u16::try_from(self.octets.as_ref().len()).expect("long OPT"))
347 }
348
349 fn compose_rdata<Target: Composer + ?Sized>(
350 &self,
351 target: &mut Target,
352 ) -> Result<(), Target::AppendError> {
353 target.append_slice(self.octets.as_ref())
354 }
355
356 fn compose_canonical_rdata<Target: Composer + ?Sized>(
357 &self,
358 target: &mut Target,
359 ) -> Result<(), Target::AppendError> {
360 self.compose_rdata(target)
361 }
362}
363
364impl<Octs: AsRef<[u8]> + ?Sized> fmt::Display for Opt<Octs> {
367 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
368 f.write_str("OPT ...")
370 }
371}
372
373impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Opt<Octs> {
374 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
375 f.write_str("Opt(")?;
376 fmt::Display::fmt(self, f)?;
377 f.write_str(")")
378 }
379}
380
381#[derive(Copy, Clone, Debug, Eq, PartialEq)]
404#[repr(transparent)]
405pub struct OptHeader {
406 inner: [u8; 9],
408}
409
410impl OptHeader {
411 #[must_use]
413 pub fn for_record_slice(slice: &[u8]) -> &OptHeader {
414 assert!(slice.len() >= mem::size_of::<Self>());
415
416 unsafe { &*(slice.as_ptr() as *const OptHeader) }
420 }
421
422 pub fn for_record_slice_mut(slice: &mut [u8]) -> &mut OptHeader {
424 assert!(slice.len() >= mem::size_of::<Self>());
425
426 unsafe { &mut *(slice.as_mut_ptr() as *mut OptHeader) }
430 }
431
432 #[must_use]
440 pub fn udp_payload_size(&self) -> u16 {
441 u16::from_be_bytes(self.inner[3..5].try_into().unwrap())
442 }
443
444 pub fn set_udp_payload_size(&mut self, value: u16) {
446 self.inner[3..5].copy_from_slice(&value.to_be_bytes())
447 }
448
449 #[must_use]
454 pub fn rcode(&self, header: Header) -> OptRcode {
455 OptRcode::from_parts(header.rcode(), self.inner[5])
456 }
457
458 pub fn set_rcode(&mut self, rcode: OptRcode) {
463 self.inner[5] = rcode.ext()
464 }
465
466 #[must_use]
470 pub fn version(&self) -> u8 {
471 self.inner[6]
472 }
473
474 pub fn set_version(&mut self, version: u8) {
476 self.inner[6] = version
477 }
478
479 #[must_use]
488 pub fn dnssec_ok(&self) -> bool {
489 self.inner[7] & 0x80 != 0
490 }
491
492 pub fn set_dnssec_ok(&mut self, value: bool) {
494 if value {
495 self.inner[7] |= 0x80
496 } else {
497 self.inner[7] &= 0x7F
498 }
499 }
500
501 pub fn compose<Target: OctetsBuilder + ?Sized>(
502 self,
503 target: &mut Target,
504 ) -> Result<(), Target::AppendError> {
505 target.append_slice(&self.inner)
506 }
507}
508
509impl Default for OptHeader {
510 fn default() -> Self {
511 OptHeader {
512 inner: [0, 0, 41, 0, 0, 0, 0, 0, 0],
513 }
514 }
515}
516
517#[derive(Clone)]
526pub struct OptRecord<Octs> {
527 udp_payload_size: u16,
529
530 ext_rcode: u8,
532
533 version: u8,
535
536 flags: u16,
538
539 data: Opt<Octs>,
541}
542
543impl<Octs> OptRecord<Octs> {
544 pub fn from_record<N: ToName>(record: Record<N, Opt<Octs>>) -> Self {
546 OptRecord {
547 udp_payload_size: record.class().to_int(),
548 ext_rcode: (record.ttl().as_secs() >> 24) as u8,
549 version: (record.ttl().as_secs() >> 16) as u8,
550 flags: record.ttl().as_secs() as u16,
551 data: record.into_data(),
552 }
553 }
554
555 pub fn as_record(&self) -> Record<&'static Name<[u8]>, Opt<&[u8]>>
557 where
558 Octs: AsRef<[u8]>,
559 {
560 Record::new(
561 Name::root_slice(),
562 Class::from_int(self.udp_payload_size),
563 Ttl::from_secs(
564 u32::from(self.ext_rcode) << 24
565 | u32::from(self.version) << 16
566 | u32::from(self.flags),
567 ),
568 self.data.for_slice_ref(),
569 )
570 }
571
572 pub fn udp_payload_size(&self) -> u16 {
580 self.udp_payload_size
581 }
582
583 pub fn set_udp_payload_size(&mut self, value: u16) {
585 self.udp_payload_size = value
586 }
587
588 pub fn rcode(&self, header: Header) -> OptRcode {
593 OptRcode::from_parts(header.rcode(), self.ext_rcode)
594 }
595
596 pub fn version(&self) -> u8 {
600 self.version
601 }
602
603 pub fn dnssec_ok(&self) -> bool {
612 self.flags & 0x8000 != 0
613 }
614
615 pub fn set_dnssec_ok(&mut self, value: bool) {
616 if value {
617 self.flags |= 0x8000;
618 } else {
619 self.flags &= !0x8000;
620 }
621 }
622
623 pub fn opt(&self) -> &Opt<Octs> {
625 &self.data
626 }
627}
628
629impl<Octs: Composer> OptRecord<Octs> {
630 pub fn push<Opt: ComposeOptData + ?Sized>(
632 &mut self,
633 option: &Opt,
634 ) -> Result<(), BuildDataError> {
635 self.data.push(option)
636 }
637
638 pub fn push_raw_option<F>(
643 &mut self,
644 code: OptionCode,
645 option_len: u16,
646 op: F,
647 ) -> Result<(), BuildDataError>
648 where
649 F: FnOnce(&mut Octs) -> Result<(), Octs::AppendError>,
650 {
651 self.data.push_raw_option(code, option_len, op)
652 }
653}
654
655impl<Octs: EmptyBuilder> Default for OptRecord<Octs> {
656 fn default() -> Self {
657 Self {
658 udp_payload_size: 0,
659 ext_rcode: 0,
660 version: 0,
661 flags: 0,
662 data: Opt::empty(),
663 }
664 }
665}
666
667impl<Octs, N: ToName> From<Record<N, Opt<Octs>>> for OptRecord<Octs> {
670 fn from(record: Record<N, Opt<Octs>>) -> Self {
671 Self::from_record(record)
672 }
673}
674
675impl<Octs, SrcOcts> OctetsFrom<OptRecord<SrcOcts>> for OptRecord<Octs>
678where
679 Octs: OctetsFrom<SrcOcts>,
680{
681 type Error = Octs::Error;
682
683 fn try_octets_from(
684 source: OptRecord<SrcOcts>,
685 ) -> Result<Self, Self::Error> {
686 Ok(OptRecord {
687 udp_payload_size: source.udp_payload_size,
688 ext_rcode: source.ext_rcode,
689 version: source.version,
690 flags: source.flags,
691 data: Opt::try_octets_from(source.data)?,
692 })
693 }
694}
695
696impl<Octs> AsRef<Opt<Octs>> for OptRecord<Octs> {
699 fn as_ref(&self) -> &Opt<Octs> {
700 &self.data
701 }
702}
703
704impl<Octs: AsRef<[u8]>> fmt::Debug for OptRecord<Octs> {
707 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
708 f.debug_struct("OptRecord")
709 .field("udp_payload_size", &self.udp_payload_size)
710 .field("ext_rcord", &self.ext_rcode)
711 .field("version", &self.version)
712 .field("flags", &self.flags)
713 .field("data", &self.data)
714 .finish()
715 }
716}
717
718#[derive(Clone, Copy, Debug)]
726pub struct OptionHeader {
727 code: u16,
729
730 len: u16,
732}
733
734#[allow(clippy::len_without_is_empty)]
735impl OptionHeader {
736 #[must_use]
738 pub fn new(code: u16, len: u16) -> Self {
739 OptionHeader { code, len }
740 }
741
742 #[must_use]
744 pub fn code(self) -> u16 {
745 self.code
746 }
747
748 #[must_use]
750 pub fn len(self) -> u16 {
751 self.len
752 }
753
754 pub fn parse<Octs: AsRef<[u8]> + ?Sized>(
755 parser: &mut Parser<Octs>,
756 ) -> Result<Self, ParseError> {
757 Ok(OptionHeader::new(
758 parser.parse_u16_be()?,
759 parser.parse_u16_be()?,
760 ))
761 }
762}
763
764#[derive(Clone, Debug)]
774pub struct OptIter<'a, Octs: ?Sized, D> {
775 parser: Parser<'a, Octs>,
777
778 marker: PhantomData<D>,
780}
781
782impl<'a, Octs, D> OptIter<'a, Octs, D>
783where
784 Octs: Octets + ?Sized,
785 D: ParseOptData<'a, Octs>,
786{
787 fn new(octets: &'a Octs) -> Self {
789 OptIter {
790 parser: Parser::from_ref(octets),
791 marker: PhantomData,
792 }
793 }
794
795 fn next_step(&mut self) -> Result<Option<D>, ParseError> {
801 let code = self.parser.parse_u16_be()?.into();
802 let len = self.parser.parse_u16_be()? as usize;
803 let mut parser = self.parser.parse_parser(len)?;
804 let res = D::parse_option(code, &mut parser)?;
805 if res.is_some() && parser.remaining() > 0 {
806 return Err(ParseError::Form(FormError::new(
807 "trailing data in option",
808 )));
809 }
810 Ok(res)
811 }
812}
813
814impl<'a, Octs, Data> Iterator for OptIter<'a, Octs, Data>
815where
816 Octs: Octets + ?Sized,
817 Data: ParseOptData<'a, Octs>,
818{
819 type Item = Result<Data, ParseError>;
820
821 fn next(&mut self) -> Option<Self::Item> {
822 while self.parser.remaining() > 0 {
823 match self.next_step() {
824 Ok(Some(res)) => return Some(Ok(res)),
825 Ok(None) => {}
826 Err(err) => {
827 self.parser.advance_to_end();
829 return Some(Err(err));
830 }
831 }
832 }
833 None
834 }
835}
836
837pub trait OptData {
844 fn code(&self) -> OptionCode;
846}
847
848pub trait ParseOptData<'a, Octs: ?Sized>: OptData + Sized {
852 fn parse_option(
866 code: OptionCode,
867 parser: &mut Parser<'a, Octs>,
868 ) -> Result<Option<Self>, ParseError>;
869}
870
871pub trait ComposeOptData: OptData {
875 fn compose_len(&self) -> u16;
876
877 fn compose_option<Target: OctetsBuilder + ?Sized>(
878 &self,
879 target: &mut Target,
880 ) -> Result<(), Target::AppendError>;
881}
882
883#[derive(Clone)]
889#[cfg_attr(feature = "serde", derive(serde::Serialize))]
890pub struct UnknownOptData<Octs> {
891 code: OptionCode,
893
894 #[cfg_attr(
896 feature = "serde",
897 serde(
898 serialize_with = "crate::utils::base16::serde::serialize",
899 bound(
900 serialize = "Octs: AsRef<[u8]> + octseq::serde::SerializeOctets",
901 )
902 )
903 )]
904 data: Octs,
905}
906
907impl<Octs> UnknownOptData<Octs> {
908 pub fn new(code: OptionCode, data: Octs) -> Result<Self, LongOptData>
912 where
913 Octs: AsRef<[u8]>,
914 {
915 LongOptData::check_len(data.as_ref().len())?;
916 Ok(unsafe { Self::new_unchecked(code, data) })
917 }
918
919 pub unsafe fn new_unchecked(code: OptionCode, data: Octs) -> Self {
926 Self { code, data }
927 }
928
929 pub fn code(&self) -> OptionCode {
931 self.code
932 }
933
934 pub fn data(&self) -> &Octs {
936 &self.data
937 }
938
939 pub fn as_slice(&self) -> &[u8]
941 where
942 Octs: AsRef<[u8]>,
943 {
944 self.data.as_ref()
945 }
946
947 pub fn as_slice_mut(&mut self) -> &mut [u8]
949 where
950 Octs: AsMut<[u8]>,
951 {
952 self.data.as_mut()
953 }
954}
955
956impl<Octs, SrcOcts> OctetsFrom<UnknownOptData<SrcOcts>>
959 for UnknownOptData<Octs>
960where
961 Octs: OctetsFrom<SrcOcts>,
962{
963 type Error = Octs::Error;
964
965 fn try_octets_from(
966 src: UnknownOptData<SrcOcts>,
967 ) -> Result<Self, Self::Error> {
968 Ok(unsafe {
969 Self::new_unchecked(src.code, Octs::try_octets_from(src.data)?)
970 })
971 }
972}
973impl<Octs> AsRef<Octs> for UnknownOptData<Octs> {
976 fn as_ref(&self) -> &Octs {
977 self.data()
978 }
979}
980
981impl<Octs: AsRef<[u8]>> AsRef<[u8]> for UnknownOptData<Octs> {
982 fn as_ref(&self) -> &[u8] {
983 self.as_slice()
984 }
985}
986
987impl<Octs: AsMut<[u8]>> AsMut<[u8]> for UnknownOptData<Octs> {
988 fn as_mut(&mut self) -> &mut [u8] {
989 self.as_slice_mut()
990 }
991}
992
993impl<Octs: AsRef<[u8]>> OptData for UnknownOptData<Octs> {
996 fn code(&self) -> OptionCode {
997 self.code
998 }
999}
1000
1001impl<'a, Octs> ParseOptData<'a, Octs> for UnknownOptData<Octs::Range<'a>>
1002where
1003 Octs: Octets + ?Sized,
1004{
1005 fn parse_option(
1006 code: OptionCode,
1007 parser: &mut Parser<'a, Octs>,
1008 ) -> Result<Option<Self>, ParseError> {
1009 Self::new(code, parser.parse_octets(parser.remaining())?)
1010 .map(Some)
1011 .map_err(Into::into)
1012 }
1013}
1014
1015impl<Octs: AsRef<[u8]>> ComposeOptData for UnknownOptData<Octs> {
1016 fn compose_len(&self) -> u16 {
1017 self.data
1018 .as_ref()
1019 .len()
1020 .try_into()
1021 .expect("long option data")
1022 }
1023
1024 fn compose_option<Target: OctetsBuilder + ?Sized>(
1025 &self,
1026 target: &mut Target,
1027 ) -> Result<(), Target::AppendError> {
1028 target.append_slice(self.data.as_ref())
1029 }
1030}
1031
1032impl<Octs: AsRef<[u8]>> fmt::Display for UnknownOptData<Octs> {
1035 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1036 base16::display(self.data.as_ref(), f)
1037 }
1038}
1039
1040impl<Octs: AsRef<[u8]>> fmt::Debug for UnknownOptData<Octs> {
1041 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1042 f.debug_struct("UnknownOptData")
1043 .field("code", &self.code)
1044 .field("data", &format_args!("{}", self))
1045 .finish()
1046 }
1047}
1048
1049#[derive(Clone, Copy, Debug)]
1055pub struct LongOptData(());
1056
1057impl LongOptData {
1058 #[must_use]
1059 pub fn as_str(self) -> &'static str {
1060 "option data too long"
1061 }
1062
1063 pub fn check_len(len: usize) -> Result<(), Self> {
1064 if len > usize::from(u16::MAX) {
1065 Err(Self(()))
1066 } else {
1067 Ok(())
1068 }
1069 }
1070}
1071
1072impl From<LongOptData> for ParseError {
1073 fn from(src: LongOptData) -> Self {
1074 ParseError::form_error(src.as_str())
1075 }
1076}
1077
1078impl fmt::Display for LongOptData {
1079 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1080 f.write_str(self.as_str())
1081 }
1082}
1083
1084#[cfg(feature = "std")]
1085impl std::error::Error for LongOptData {}
1086
1087#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1091pub enum BuildDataError {
1092 LongOptData,
1094
1095 ShortBuf,
1097}
1098
1099impl BuildDataError {
1100 pub fn unlimited_buf(self) -> LongOptData {
1106 match self {
1107 Self::LongOptData => LongOptData(()),
1108 Self::ShortBuf => panic!("ShortBuf on unlimited buffer"),
1109 }
1110 }
1111}
1112
1113impl From<LongOptData> for BuildDataError {
1114 fn from(_: LongOptData) -> Self {
1115 Self::LongOptData
1116 }
1117}
1118
1119impl<T: Into<ShortBuf>> From<T> for BuildDataError {
1120 fn from(_: T) -> Self {
1121 Self::ShortBuf
1122 }
1123}
1124
1125impl fmt::Display for BuildDataError {
1128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1129 match self {
1130 Self::LongOptData => f.write_str("long option data"),
1131 Self::ShortBuf => ShortBuf.fmt(f),
1132 }
1133 }
1134}
1135
1136#[cfg(feature = "std")]
1137impl std::error::Error for BuildDataError {}
1138
1139#[cfg(test)]
1142#[cfg(all(feature = "std", feature = "bytes"))]
1143pub(super) mod test {
1144 use super::*;
1145 use crate::base::rdata::test::{test_compose_parse, test_rdlen};
1146 use crate::base::record::ParsedRecord;
1147 use crate::base::{opt, MessageBuilder};
1148 use bytes::{Bytes, BytesMut};
1149 use core::fmt::Debug;
1150 use octseq::builder::infallible;
1151 use std::vec::Vec;
1152
1153 #[test]
1154 #[allow(clippy::redundant_closure)] fn opt_compose_parse_scan() {
1156 let rdata = Opt::from_octets("fo\x00\x03foo").unwrap();
1157 test_rdlen(&rdata);
1158 test_compose_parse(&rdata, |parser| Opt::parse(parser));
1159 }
1160
1161 #[test]
1162 fn opt_record_header() {
1163 let mut header = OptHeader::default();
1164 header.set_udp_payload_size(0x1234);
1165 header.set_rcode(OptRcode::BADVERS);
1166 header.set_version(0xbd);
1167 header.set_dnssec_ok(true);
1168 let mut buf = Vec::with_capacity(11);
1169 infallible(header.compose(&mut buf));
1170 infallible(0u16.compose(&mut buf));
1171 let mut buf = Parser::from_ref(buf.as_slice());
1172 let record = ParsedRecord::parse(&mut buf)
1173 .unwrap()
1174 .into_record::<Opt<_>>()
1175 .unwrap()
1176 .unwrap();
1177 let record = OptRecord::from_record(record);
1178 assert_eq!(record.udp_payload_size(), 0x1234);
1179 assert_eq!(record.ext_rcode, OptRcode::BADVERS.ext());
1180 assert_eq!(record.version(), 0xbd);
1181 assert!(record.dnssec_ok());
1182 }
1183
1184 #[test]
1185 fn opt_iter() {
1186 use self::opt::cookie::{ClientCookie, Cookie};
1187
1188 let nsid = opt::Nsid::from_octets(&b"example"[..]).unwrap();
1190 let cookie = Cookie::new(
1191 ClientCookie::from_octets(1234u64.to_be_bytes()),
1192 None,
1193 );
1194 let msg = {
1195 let mut mb = MessageBuilder::new_vec().additional();
1196 mb.opt(|mb| {
1197 mb.push(&nsid)?;
1198 mb.push(&cookie)?;
1199 Ok(())
1200 })
1201 .unwrap();
1202 mb.into_message()
1203 };
1204
1205 let opt = msg.opt().unwrap();
1207 assert_eq!(Some(Ok(nsid)), opt.opt().iter::<opt::Nsid<_>>().next());
1208 assert_eq!(Some(Ok(cookie)), opt.opt().iter::<opt::Cookie>().next());
1209 }
1210
1211 pub fn test_option_compose_parse<In, F, Out>(data: &In, parse: F)
1212 where
1213 In: ComposeOptData + PartialEq<Out> + Debug,
1214 F: FnOnce(&mut Parser<Bytes>) -> Result<Out, ParseError>,
1215 Out: Debug,
1216 {
1217 let mut buf = BytesMut::new();
1218 infallible(data.compose_option(&mut buf));
1219 let buf = buf.freeze();
1220 assert_eq!(buf.len(), usize::from(data.compose_len()));
1221 let mut parser = Parser::from_ref(&buf);
1222 let parsed = (parse)(&mut parser).unwrap();
1223 assert_eq!(parser.remaining(), 0);
1224 assert_eq!(*data, parsed);
1225 }
1226}