1use core::ops::Range;
2use core::{
3 marker::PhantomData,
4 net::{IpAddr, Ipv4Addr, Ipv6Addr},
5};
6
7use byteorder::{ByteOrder, NetworkEndian};
8
9use self::error::{RdataError, RdataResultExt, SoaError};
10use crate::view::{assert, BorrowedView, CharacterString, CharacterStrings, Name, Record, View};
11use crate::{
12 core::{Class, Serial, Ttl, Type},
13 fmt::Format,
14 rdata::class::{GenericClass, InClass},
15};
16
17macro_rules! declare_rdata {
18 ($($variant:ident($($ty:tt)+),)*) => {
19 #[non_exhaustive]
20 pub enum Rdata<'s> {
21 $($variant($($ty)+<'s>),)*
22 }
23 impl<'s> From<&Record<'s>> for Rdata<'s> {
24 fn from(record: &Record<'s>) -> Self {
25 let range = record.rdata_range();
26
27 if let Ok(result) = || -> Result<Self, ()> {Ok(match (record.class(), record.r#type()) {
28 (_, Type::NS) => Ns::view(record.source(), range).map_err(|_| ())?.0.into(),
29 (_, Type::CNAME) => Cname::view(record.source(), range).map_err(|_| ())?.0.into(),
30 (_, Type::SOA) => Soa::view(record.source(), range).map_err(|_| ())?.0.into(),
31 (_, Type::PTR) => Ptr::view(record.source(), range).map_err(|_| ())?.0.into(),
32 (_, Type::MX) => Mx::view(record.source(), range).map_err(|_| ())?.0.into(),
33 (_, Type::TXT) => Txt::view(record.source(), range).map_err(|_| ())?.0.into(),
34 (_, Type::CAA) => Caa::view(record.source(), range).map_err(|_| ())?.0.into(),
35 (Class::IN, Type::A) => A::view(record.source(), range).map_err(|_| ())?.0.into(),
36 (Class::IN, Type::AAAA) => Aaaa::view(record.source(), range).map_err(|_| ())?.0.into(),
37 _ => Unknown::view(record.source(), range).map_err(|_| ())?.0.into(),
38 })}() {
39 return result;
40 }
41
42 Malformed::view(record.source(), record.rdata_range())
43 .expect("guaranteed by impl View for Record")
44 .0
45 .into()
46 }
47 }
48 $(impl<'s> From<$($ty)+<'s>> for Rdata<'s> {
49 fn from(inner: $($ty)+<'s>) -> Self {
50 Self::$variant(inner)
51 }
52 })*
53 impl<'s> AsRef<dyn View<'s, Error = RdataError>> for Rdata<'s> {
54 fn as_ref(&self) -> &dyn View<'s, Error = RdataError> {
55 match self {
56 $(Self::$variant(x) => x,)*
57 }
58 }
59 }
60 impl<'s> AsRef<dyn Format + 's> for Rdata<'s> {
61 fn as_ref(&self) -> &(dyn Format + 's) {
62 match self {
63 $(Self::$variant(x) => x,)*
64 }
65 }
66 }
67 };
68}
69
70declare_rdata! {
71 Malformed(Malformed),
72 Unknown(Unknown),
73 Ns(Ns),
74 Cname(Cname),
75 Soa(Soa),
76 Ptr(Ptr),
77 Mx(Mx),
78 Txt(Txt),
79 Caa(Caa),
80 InAddress(A),
81 InAaaa(Aaaa),
82}
83
84pub struct Ns<'s, C: GenericClass = InClass> {
86 inner: Name<'s>,
87 _class: PhantomData<C>,
88}
89
90pub struct Cname<'s, C: GenericClass = InClass> {
92 inner: Name<'s>,
93 _class: PhantomData<C>,
94}
95
96pub struct Soa<'s, C: GenericClass = InClass> {
98 source: &'s [u8],
99 offset: usize,
100 mname_len: usize,
101 rname_len: usize,
102 _class: PhantomData<C>,
103}
104
105pub struct Ptr<'s, C: GenericClass = InClass> {
107 inner: Name<'s>,
108 _class: PhantomData<C>,
109}
110
111pub struct Mx<'s, C: GenericClass = InClass> {
113 source: &'s [u8],
114 offset: usize,
115 exchange_len: usize,
116 _class: PhantomData<C>,
117}
118
119pub struct Txt<'s, C: GenericClass = InClass> {
121 source: &'s [u8],
122 offset: usize,
123 len: usize,
124 _class: PhantomData<C>,
125}
126
127pub struct Caa<'s, C: GenericClass = InClass> {
129 source: &'s [u8],
130 offset: usize,
131 tag_len: usize,
132 value_len: usize,
133 _class: PhantomData<C>,
134}
135
136pub struct CaaTag<'s, C: GenericClass = InClass> {
138 source: &'s [u8],
139 offset: usize,
140 len: usize,
141 _class: PhantomData<C>,
142}
143
144pub struct CaaValue<'s, C: GenericClass = InClass> {
146 source: &'s [u8],
147 offset: usize,
148 len: usize,
149 _class: PhantomData<C>,
150}
151
152pub struct Malformed<'s, C: GenericClass = InClass> {
154 inner: Unknown<'s>,
155 _class: PhantomData<C>,
156}
157
158#[derive(Clone)]
160pub struct Unknown<'s, C: GenericClass = InClass> {
161 source: &'s [u8],
162 offset: usize,
163 len: usize,
164 _class: PhantomData<C>,
165}
166
167impl<C: GenericClass> Ns<'_, C> {
168 pub fn name(&self) -> Name {
169 self.inner.clone()
170 }
171}
172
173impl<C: GenericClass> Cname<'_, C> {
174 pub fn name(&self) -> Name {
175 self.inner.clone()
176 }
177}
178
179impl<C: GenericClass> Soa<'_, C> {
180 pub fn mname(&self) -> Name {
181 let start = self.offset;
182 let end = start + self.mname_len;
183 Name::new_unchecked(self.source, start..end)
184 }
185
186 pub fn rname(&self) -> Name {
187 let start = self.offset + self.mname_len;
188 let end = start + self.rname_len;
189 Name::new_unchecked(self.source, start..end)
190 }
191
192 pub fn serial(&self) -> Serial {
193 let offset = self.mname_len + self.rname_len;
194
195 Serial::new(NetworkEndian::read_u32(
196 &self.source[self.offset..][offset..],
197 ))
198 }
199
200 pub fn refresh(&self) -> Ttl {
201 let offset = self.mname_len + self.rname_len + 4;
202
203 Ttl::new(NetworkEndian::read_u32(
204 &self.source[self.offset..][offset..],
205 ))
206 }
207
208 pub fn retry(&self) -> Ttl {
209 let offset = self.mname_len + self.rname_len + 8;
210
211 Ttl::new(NetworkEndian::read_u32(
212 &self.source[self.offset..][offset..],
213 ))
214 }
215
216 pub fn expire(&self) -> Ttl {
217 let offset = self.mname_len + self.rname_len + 12;
218
219 Ttl::new(NetworkEndian::read_u32(
220 &self.source[self.offset..][offset..],
221 ))
222 }
223
224 pub fn minimum(&self) -> Ttl {
225 let offset = self.mname_len + self.rname_len + 16;
226
227 Ttl::new(NetworkEndian::read_u32(
228 &self.source[self.offset..][offset..],
229 ))
230 }
231}
232
233impl<C: GenericClass> Ptr<'_, C> {
234 pub fn name(&self) -> Name {
235 self.inner.clone()
236 }
237}
238
239impl Mx<'_, InClass> {
240 pub fn preference(&self) -> u16 {
241 NetworkEndian::read_u16(&self.source[self.offset..])
242 }
243
244 pub fn exchange(&self) -> Name {
245 let start = self.offset + 2;
246 let end = start + self.exchange_len;
247 Name::new_unchecked(self.source, start..end)
248 }
249}
250
251impl Txt<'_, InClass> {
252 pub fn data(&self) -> CharacterStrings<'_> {
253 let start = self.offset;
254 let stop = self.offset + self.len;
255
256 CharacterStrings::new(self.source, start..stop)
257 }
258}
259
260impl Caa<'_, InClass> {
261 pub fn flags(&self) -> u8 {
262 self.source[self.offset]
263 }
264
265 pub fn critical(&self) -> bool {
266 self.flags() >> 7 & 1 == 1
267 }
268
269 pub fn tag(&self) -> CaaTag {
270 CaaTag {
271 source: self.source,
272 offset: self.offset + 2,
273 len: self.tag_len,
274 _class: self._class,
275 }
276 }
277
278 pub fn value(&self) -> CaaValue {
279 CaaValue {
280 source: self.source,
281 offset: self.offset + 2 + self.tag_len,
282 len: self.value_len,
283 _class: self._class,
284 }
285 }
286}
287
288impl<C: GenericClass> Malformed<'_, C> {
289 pub fn as_unknown(&self) -> Unknown {
290 self.inner.clone()
291 }
292}
293
294impl<C: GenericClass> Unknown<'_, C> {
295 pub fn as_bytes(&self) -> &[u8] {
296 &self.source[self.offset..][..self.len()]
297 }
298}
299
300impl<'s, C: GenericClass + 's> View<'s> for Ns<'s, C> {
301 type Error = RdataError;
302 fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
303 let (name, rest) = Name::view(source, range.clone()).into_rdata_error::<Self>()?;
304 Ok((
305 Self {
306 inner: name,
307 _class: PhantomData,
308 },
309 rest,
310 ))
311 }
312}
313impl<'s, C: GenericClass> BorrowedView<'s> for Ns<'s, C> {
314 fn source(&self) -> &'s [u8] {
315 self.inner.source()
316 }
317 fn offset(&self) -> usize {
318 self.inner.offset()
319 }
320 fn len(&self) -> usize {
321 self.inner.len()
322 }
323}
324
325impl<'s, C: GenericClass + 's> View<'s> for Cname<'s, C> {
326 type Error = RdataError;
327 fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
328 let (name, rest) = Name::view(source, range.clone()).into_rdata_error::<Self>()?;
329 Ok((
330 Self {
331 inner: name,
332 _class: PhantomData,
333 },
334 rest,
335 ))
336 }
337}
338impl<'s, C: GenericClass> BorrowedView<'s> for Cname<'s, C> {
339 fn source(&self) -> &'s [u8] {
340 self.inner.source()
341 }
342 fn offset(&self) -> usize {
343 self.inner.offset()
344 }
345 fn len(&self) -> usize {
346 self.inner.len()
347 }
348}
349
350impl<'s, C: GenericClass + 's> View<'s> for Soa<'s, C> {
351 type Error = RdataError;
352
353 fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
354 let (_, rest) = Name::view(source, range.clone())
355 .map_err(SoaError::Name)
356 .into_rdata_error::<Self>()?;
357 let mname_len = rest.start - range.start;
358
359 let (_, mut rest) = Name::view(source, rest)
360 .map_err(SoaError::Name)
361 .into_rdata_error::<Self>()?;
362 let rname_len = rest.start - range.start - mname_len;
363
364 assert(source, &rest, 20)
365 .map_err(SoaError::Bounds)
366 .into_rdata_error::<Self>()?;
367 rest.start += 20;
368
369 Ok((
370 Soa {
371 source,
372 offset: range.start,
373 mname_len,
374 rname_len,
375 _class: PhantomData,
376 },
377 rest,
378 ))
379 }
380}
381impl<'s, C: GenericClass> BorrowedView<'s> for Soa<'s, C> {
382 fn source(&self) -> &'s [u8] {
383 self.source
384 }
385 fn offset(&self) -> usize {
386 self.offset
387 }
388 fn len(&self) -> usize {
389 self.mname_len + self.rname_len + 20
390 }
391}
392
393impl<'s, C: GenericClass + 's> View<'s> for Ptr<'s, C> {
394 type Error = RdataError;
395 fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
396 let (name, rest) = Name::view(source, range.clone()).into_rdata_error::<Self>()?;
397 Ok((
398 Self {
399 inner: name,
400 _class: PhantomData,
401 },
402 rest,
403 ))
404 }
405}
406impl<'s, C: GenericClass> BorrowedView<'s> for Ptr<'s, C> {
407 fn source(&self) -> &'s [u8] {
408 self.inner.source()
409 }
410 fn offset(&self) -> usize {
411 self.inner.offset()
412 }
413 fn len(&self) -> usize {
414 self.inner.len()
415 }
416}
417
418impl<'s> View<'s> for Mx<'s, InClass> {
419 type Error = RdataError;
420
421 fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
422 let mut rest = range.clone();
423
424 assert(source, &rest, 2).into_rdata_error::<Self>()?;
425 rest.start += 2;
426
427 let (_, rest) = Name::view(source, rest)
428 .map_err(SoaError::Name)
429 .into_rdata_error::<Self>()?;
430 let exchange_len = rest.start - range.start - 2;
431
432 Ok((
433 Mx {
434 source,
435 offset: range.start,
436 exchange_len,
437 _class: PhantomData,
438 },
439 rest,
440 ))
441 }
442}
443impl<'s> BorrowedView<'s> for Mx<'s, InClass> {
444 fn source(&self) -> &'s [u8] {
445 self.source
446 }
447 fn offset(&self) -> usize {
448 self.offset
449 }
450 fn len(&self) -> usize {
451 2 + self.exchange_len
452 }
453}
454
455impl<'s> View<'s> for Txt<'s, InClass> {
456 type Error = RdataError;
457
458 fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
459 let mut next = range.clone();
460
461 while next.start != next.end {
462 let (_, rest) = CharacterString::view(source, next).into_rdata_error::<Self>()?;
463 next = rest;
464 }
465
466 let offset = range.start;
467 let len = next.start - offset;
468
469 Ok((
470 Txt {
471 source,
472 offset,
473 len,
474 _class: PhantomData,
475 },
476 next,
477 ))
478 }
479}
480impl<'s> BorrowedView<'s> for Txt<'s, InClass> {
481 fn source(&self) -> &'s [u8] {
482 self.source
483 }
484 fn offset(&self) -> usize {
485 self.offset
486 }
487 fn len(&self) -> usize {
488 self.len
489 }
490}
491
492impl<'s> View<'s> for Caa<'s, InClass> {
493 type Error = RdataError;
494
495 fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
496 let mut rest = range.clone();
497
498 assert(source, &rest, 1).into_rdata_error::<Self>()?;
499 rest.start += 1;
500
501 let (octet, mut rest) = u8::view(source, rest).into_rdata_error::<Self>()?;
502 let tag_len: usize = octet.into();
503
504 assert(source, &rest, tag_len).into_rdata_error::<Self>()?;
505 rest.start += tag_len;
506
507 let value_len = rest.end - rest.start;
509 rest.start += value_len;
510
511 Ok((
512 Caa {
513 source,
514 offset: range.start,
515 tag_len,
516 value_len,
517 _class: PhantomData,
518 },
519 rest,
520 ))
521 }
522}
523impl<'s> BorrowedView<'s> for Caa<'s, InClass> {
524 fn source(&self) -> &'s [u8] {
525 self.source
526 }
527 fn offset(&self) -> usize {
528 self.offset
529 }
530 fn len(&self) -> usize {
531 1 + 1 + self.tag_len + self.value_len
532 }
533}
534
535impl<'s> BorrowedView<'s> for CaaTag<'s, InClass> {
536 fn source(&self) -> &'s [u8] {
537 self.source
538 }
539 fn offset(&self) -> usize {
540 self.offset
541 }
542 fn len(&self) -> usize {
543 self.len
544 }
545}
546
547impl<'s> BorrowedView<'s> for CaaValue<'s, InClass> {
548 fn source(&self) -> &'s [u8] {
549 self.source
550 }
551 fn offset(&self) -> usize {
552 self.offset
553 }
554 fn len(&self) -> usize {
555 self.len
556 }
557}
558
559impl<'s, C: GenericClass + 's> View<'s> for Malformed<'s, C> {
560 type Error = RdataError;
561
562 fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
563 let (inner, range) = Unknown::view(source, range)?;
564
565 Ok((
566 Malformed {
567 inner,
568 _class: PhantomData,
569 },
570 range,
571 ))
572 }
573}
574impl<'s, C: GenericClass> BorrowedView<'s> for Malformed<'s, C> {
575 fn source(&self) -> &'s [u8] {
576 self.inner.source()
577 }
578 fn offset(&self) -> usize {
579 self.inner.offset()
580 }
581 fn len(&self) -> usize {
582 self.inner.len()
583 }
584}
585
586impl<'s, C: GenericClass + 's> View<'s> for Unknown<'s, C> {
587 type Error = RdataError;
588
589 fn view_range(
590 source: &'s [u8],
591 mut range: Range<usize>,
592 ) -> crate::view::Result<Self, Self::Error> {
593 let offset = range.start;
594 let len = range.end - offset;
595
596 assert(source, &range, len).into_rdata_error::<Self>()?;
597 range.start += len;
598
599 Ok((
600 Unknown {
601 source,
602 offset,
603 len,
604 _class: PhantomData,
605 },
606 range,
607 ))
608 }
609}
610impl<'s, C: GenericClass> BorrowedView<'s> for Unknown<'s, C> {
611 fn source(&self) -> &'s [u8] {
612 self.source
613 }
614 fn offset(&self) -> usize {
615 self.offset
616 }
617 fn len(&self) -> usize {
618 self.len
619 }
620}
621
622pub struct A<'s, C: GenericClass = InClass> {
624 source: &'s [u8],
625 pub offset: usize,
626 _class: PhantomData<C>,
627}
628
629pub struct Aaaa<'s, C: GenericClass = InClass> {
631 source: &'s [u8],
632 pub offset: usize,
633 _class: PhantomData<C>,
634}
635
636impl A<'_, InClass> {
637 pub fn to_ipv4_addr(&self) -> Ipv4Addr {
638 let bits = NetworkEndian::read_u32(&self.source[self.offset..][..self.len()]);
639 Ipv4Addr::from_bits(bits)
640 }
641
642 pub fn to_ip_addr(&self) -> IpAddr {
643 self.to_ipv4_addr().into()
644 }
645}
646
647impl From<A<'_, InClass>> for Ipv4Addr {
648 fn from(value: A<'_>) -> Self {
649 value.to_ipv4_addr()
650 }
651}
652
653impl From<A<'_, InClass>> for IpAddr {
654 fn from(value: A<'_>) -> Self {
655 value.to_ip_addr()
656 }
657}
658
659impl Aaaa<'_, InClass> {
660 pub fn to_ipv6_addr(&self) -> Ipv6Addr {
661 let bits = NetworkEndian::read_u128(&self.source[self.offset..][..self.len()]);
662 Ipv6Addr::from_bits(bits)
663 }
664
665 pub fn to_ip_addr(&self) -> IpAddr {
666 self.to_ipv6_addr().into()
667 }
668}
669
670impl From<Aaaa<'_, InClass>> for Ipv6Addr {
671 fn from(value: Aaaa<'_>) -> Self {
672 value.to_ipv6_addr()
673 }
674}
675
676impl From<Aaaa<'_, InClass>> for IpAddr {
677 fn from(value: Aaaa<'_>) -> Self {
678 value.to_ip_addr()
679 }
680}
681
682impl<'s> View<'s> for A<'s, InClass> {
683 type Error = RdataError;
684
685 fn view_range(
686 source: &'s [u8],
687 mut range: Range<usize>,
688 ) -> crate::view::Result<Self, Self::Error> {
689 let offset = range.start;
690
691 assert(source, &range, 4).into_rdata_error::<Self>()?;
692 range.start += 4;
693
694 Ok((
695 A {
696 source,
697 offset,
698 _class: PhantomData,
699 },
700 range,
701 ))
702 }
703}
704impl<'s> BorrowedView<'s> for A<'s, InClass> {
705 fn source(&self) -> &'s [u8] {
706 self.source
707 }
708 fn offset(&self) -> usize {
709 self.offset
710 }
711 fn len(&self) -> usize {
712 4
713 }
714}
715
716impl<'s> View<'s> for Aaaa<'s, InClass> {
717 type Error = RdataError;
718
719 fn view_range(
720 source: &'s [u8],
721 mut range: Range<usize>,
722 ) -> crate::view::Result<Self, Self::Error> {
723 let offset = range.start;
724
725 assert(source, &range, 16).into_rdata_error::<Self>()?;
726 range.start += 16;
727
728 Ok((
729 Aaaa {
730 source,
731 offset,
732 _class: PhantomData,
733 },
734 range,
735 ))
736 }
737}
738impl<'s> BorrowedView<'s> for Aaaa<'s, InClass> {
739 fn source(&self) -> &'s [u8] {
740 self.source
741 }
742 fn offset(&self) -> usize {
743 self.offset
744 }
745 fn len(&self) -> usize {
746 16
747 }
748}
749
750pub mod error {
751 use crate::view::{BoundsError, NameError};
752
753 error!(TypedRdataError, View);
754 #[derive(Debug, displaydoc::Display)]
755 pub enum TypedRdataError {
756 View(RdataError),
758 WrongClassOrType,
760 }
761
762 #[cfg(feature = "eyre")]
763 error!(RdataError([s: s.as_ref()], __));
764 #[cfg(not(feature = "eyre"))]
765 error!(RdataError);
766 #[derive(Debug, displaydoc::Display)]
767 pub struct RdataError(RdataError0, &'static str);
769
770 #[cfg(feature = "eyre")]
771 type RdataError0 = eyre::Report;
772 #[cfg(not(feature = "eyre"))]
773 type RdataError0 = ();
774
775 error!(SoaError, Bounds, Name);
776 #[derive(Debug, displaydoc::Display)]
778 #[prefix_enum_doc_attributes]
779 pub enum SoaError {
780 Bounds(BoundsError),
782
783 Name(NameError),
785 }
786
787 impl RdataError {
788 #[cfg(feature = "eyre")]
789 fn new<E: 'static + Send + Sync + core::error::Error>(error: E, ty: &'static str) -> Self {
790 Self(error.into(), ty)
791 }
792 #[cfg(not(feature = "eyre"))]
793 fn new<E: 'static + Send + Sync + core::error::Error>(_: E, ty: &'static str) -> Self {
794 Self((), ty)
795 }
796 }
797
798 pub(crate) trait RdataResultExt<T> {
799 fn into_rdata_error<V>(self) -> Result<T, RdataError>;
800 }
801
802 #[cfg(feature = "eyre")]
803 impl<T, E: 'static + Send + Sync + core::error::Error> RdataResultExt<T> for Result<T, E> {
804 fn into_rdata_error<V>(self) -> Result<T, RdataError> {
805 self.map_err(|e| RdataError::new(e, core::any::type_name::<V>()))
806 }
807 }
808 #[cfg(not(feature = "eyre"))]
809 impl<T, E: 'static + Send + Sync + core::error::Error> RdataResultExt<T> for Result<T, E> {
810 fn into_rdata_error<V>(self) -> Result<T, RdataError> {
811 self.map_err(|e| RdataError::new(e, core::any::type_name::<V>()))
812 }
813 }
814}
815
816#[cfg(test)]
817mod test {
818 use crate::{
819 core::Serial,
820 rdata::{
821 class::InClass,
822 view::{Caa, Mx, Soa},
823 },
824 view::{BorrowedView, View},
825 };
826
827 declare_any_error!(AnyError);
828
829 #[test]
830 fn soa() -> Result<(), AnyError> {
831 let soa = b"\x02ns\x07example\0\x0Ahostmaster\xC0\x03\0\0\0\x01\0\0\0\x02\0\0\0\x03\0\0\0\x04\0\0\0\x05";
832 let (soa, _) = Soa::<InClass>::view(soa, ..)?;
833 assert_eq!(soa.mname(), "ns.example.");
834 assert_eq!(soa.mname().len(), 12);
835 assert_eq!(soa.rname(), "hostmaster.example.");
836 assert_eq!(soa.rname().len(), 13);
837 assert_eq!(soa.serial(), Serial::new(1));
838 assert_eq!(soa.refresh().value(), 2);
839 assert_eq!(soa.retry().value(), 3);
840 assert_eq!(soa.expire().value(), 4);
841 assert_eq!(soa.minimum().value(), 5);
842 Ok(())
843 }
844
845 #[test]
846 fn mx() -> Result<(), AnyError> {
847 let mx = b"\xAA\x55\x04mail\x07example\0";
848 let (mx, _) = Mx::view(mx, ..)?;
849 assert_eq!(mx.preference(), 0xAA55);
850 assert_eq!(mx.exchange(), "mail.example.");
851 assert_eq!(mx.exchange().len(), 14);
852 Ok(())
853 }
854
855 #[test]
856 fn caa() -> Result<(), AnyError> {
857 let caa = b"\x80\x05issueletsencrypt.org";
858 let (caa, _) = Caa::view(caa, ..)?;
859 assert_eq!(caa.critical(), true);
860 assert_eq!(caa.tag().as_bytes(), b"issue");
861 assert_eq!(caa.value().as_bytes(), b"letsencrypt.org");
862 Ok(())
863 }
864}