1use std::fmt;
49use std::net::{Ipv4Addr, Ipv6Addr};
50
51use super::dns_opt::{OptParseError, OptValue, parse_opt_value};
52use super::enums::{DnsCls, DnsRecordType, DnsRrKey};
53use super::rr::DnsRr;
54
55macro_rules! common_accessors {
59 ($struct:ident) => {
60 pub(super) fn new(rr: &'a DnsRr) -> Self {
62 Self(rr)
63 }
64
65 pub fn as_dns_rr(self) -> &'a DnsRr {
71 self.0
72 }
73
74 pub fn name(self) -> &'a str {
76 self.0.name()
77 }
78
79 pub fn dns_class(self) -> DnsCls {
81 self.0.dns_class()
82 }
83
84 pub fn ttl(self) -> u32 {
86 self.0.ttl()
87 }
88 };
89}
90
91#[derive(Copy, Clone)]
97pub struct ARecord<'a>(&'a DnsRr);
98
99impl<'a> ARecord<'a> {
100 common_accessors!(ARecord);
101
102 pub fn addr(self) -> Ipv4Addr {
107 self.0
108 .get_addr(DnsRrKey::A_ADDR)
109 .unwrap_or(Ipv4Addr::UNSPECIFIED)
110 }
111}
112
113impl fmt::Debug for ARecord<'_> {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 f.debug_struct("ARecord")
116 .field("name", &self.name())
117 .field("ttl", &self.ttl())
118 .field("addr", &self.addr())
119 .finish()
120 }
121}
122
123#[derive(Copy, Clone)]
125pub struct AaaaRecord<'a>(&'a DnsRr);
126
127impl<'a> AaaaRecord<'a> {
128 common_accessors!(AaaaRecord);
129
130 pub fn addr(self) -> Ipv6Addr {
135 self.0
136 .get_addr6(DnsRrKey::AAAA_ADDR)
137 .unwrap_or(Ipv6Addr::UNSPECIFIED)
138 }
139}
140
141impl fmt::Debug for AaaaRecord<'_> {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 f.debug_struct("AaaaRecord")
144 .field("name", &self.name())
145 .field("ttl", &self.ttl())
146 .field("addr", &self.addr())
147 .finish()
148 }
149}
150
151#[derive(Copy, Clone)]
153pub struct NsRecord<'a>(&'a DnsRr);
154
155impl<'a> NsRecord<'a> {
156 common_accessors!(NsRecord);
157
158 pub fn nsdname(self) -> &'a str {
161 self.0.get_str(DnsRrKey::NS_NSDNAME).unwrap_or("")
162 }
163}
164
165impl fmt::Debug for NsRecord<'_> {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 f.debug_struct("NsRecord")
168 .field("name", &self.name())
169 .field("ttl", &self.ttl())
170 .field("nsdname", &self.nsdname())
171 .finish()
172 }
173}
174
175#[derive(Copy, Clone)]
177pub struct CnameRecord<'a>(&'a DnsRr);
178
179impl<'a> CnameRecord<'a> {
180 common_accessors!(CnameRecord);
181
182 pub fn cname(self) -> &'a str {
184 self.0.get_str(DnsRrKey::CNAME_CNAME).unwrap_or("")
185 }
186}
187
188impl fmt::Debug for CnameRecord<'_> {
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 f.debug_struct("CnameRecord")
191 .field("name", &self.name())
192 .field("ttl", &self.ttl())
193 .field("cname", &self.cname())
194 .finish()
195 }
196}
197
198#[derive(Copy, Clone)]
200pub struct SoaRecord<'a>(&'a DnsRr);
201
202impl<'a> SoaRecord<'a> {
203 common_accessors!(SoaRecord);
204
205 pub fn mname(self) -> &'a str {
207 self.0.get_str(DnsRrKey::SOA_MNAME).unwrap_or("")
208 }
209
210 pub fn rname(self) -> &'a str {
212 self.0.get_str(DnsRrKey::SOA_RNAME).unwrap_or("")
213 }
214
215 pub fn serial(self) -> u32 {
217 self.0.get_u32(DnsRrKey::SOA_SERIAL)
218 }
219
220 pub fn refresh(self) -> u32 {
222 self.0.get_u32(DnsRrKey::SOA_REFRESH)
223 }
224
225 pub fn retry(self) -> u32 {
227 self.0.get_u32(DnsRrKey::SOA_RETRY)
228 }
229
230 pub fn expire(self) -> u32 {
232 self.0.get_u32(DnsRrKey::SOA_EXPIRE)
233 }
234
235 pub fn minimum(self) -> u32 {
237 self.0.get_u32(DnsRrKey::SOA_MINIMUM)
238 }
239}
240
241impl fmt::Debug for SoaRecord<'_> {
242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 f.debug_struct("SoaRecord")
244 .field("name", &self.name())
245 .field("ttl", &self.ttl())
246 .field("mname", &self.mname())
247 .field("rname", &self.rname())
248 .field("serial", &self.serial())
249 .field("refresh", &self.refresh())
250 .field("retry", &self.retry())
251 .field("expire", &self.expire())
252 .field("minimum", &self.minimum())
253 .finish()
254 }
255}
256
257#[derive(Copy, Clone)]
259pub struct PtrRecord<'a>(&'a DnsRr);
260
261impl<'a> PtrRecord<'a> {
262 common_accessors!(PtrRecord);
263
264 pub fn dname(self) -> &'a str {
266 self.0.get_str(DnsRrKey::PTR_DNAME).unwrap_or("")
267 }
268}
269
270impl fmt::Debug for PtrRecord<'_> {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 f.debug_struct("PtrRecord")
273 .field("name", &self.name())
274 .field("ttl", &self.ttl())
275 .field("dname", &self.dname())
276 .finish()
277 }
278}
279
280#[derive(Copy, Clone)]
282pub struct HinfoRecord<'a>(&'a DnsRr);
283
284impl<'a> HinfoRecord<'a> {
285 common_accessors!(HinfoRecord);
286
287 pub fn cpu(self) -> &'a str {
289 self.0.get_str(DnsRrKey::HINFO_CPU).unwrap_or("")
290 }
291
292 pub fn os(self) -> &'a str {
294 self.0.get_str(DnsRrKey::HINFO_OS).unwrap_or("")
295 }
296}
297
298impl fmt::Debug for HinfoRecord<'_> {
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 f.debug_struct("HinfoRecord")
301 .field("name", &self.name())
302 .field("ttl", &self.ttl())
303 .field("cpu", &self.cpu())
304 .field("os", &self.os())
305 .finish()
306 }
307}
308
309#[derive(Copy, Clone)]
311pub struct MxRecord<'a>(&'a DnsRr);
312
313impl<'a> MxRecord<'a> {
314 common_accessors!(MxRecord);
315
316 pub fn preference(self) -> u16 {
318 self.0.get_u16(DnsRrKey::MX_PREFERENCE)
319 }
320
321 pub fn exchange(self) -> &'a str {
323 self.0.get_str(DnsRrKey::MX_EXCHANGE).unwrap_or("")
324 }
325}
326
327impl fmt::Debug for MxRecord<'_> {
328 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329 f.debug_struct("MxRecord")
330 .field("name", &self.name())
331 .field("ttl", &self.ttl())
332 .field("preference", &self.preference())
333 .field("exchange", &self.exchange())
334 .finish()
335 }
336}
337
338#[derive(Copy, Clone)]
340pub struct SigRecord<'a>(&'a DnsRr);
341
342impl<'a> SigRecord<'a> {
343 common_accessors!(SigRecord);
344
345 pub fn type_covered(self) -> u16 {
347 self.0.get_u16(DnsRrKey::SIG_TYPE_COVERED)
348 }
349
350 pub fn algorithm(self) -> u8 {
352 self.0.get_u8(DnsRrKey::SIG_ALGORITHM)
353 }
354
355 pub fn labels(self) -> u8 {
357 self.0.get_u8(DnsRrKey::SIG_LABELS)
358 }
359
360 pub fn original_ttl(self) -> u32 {
362 self.0.get_u32(DnsRrKey::SIG_ORIGINAL_TTL)
363 }
364
365 pub fn expiration(self) -> u32 {
368 self.0.get_u32(DnsRrKey::SIG_EXPIRATION)
369 }
370
371 pub fn inception(self) -> u32 {
374 self.0.get_u32(DnsRrKey::SIG_INCEPTION)
375 }
376
377 pub fn key_tag(self) -> u16 {
379 self.0.get_u16(DnsRrKey::SIG_KEY_TAG)
380 }
381
382 pub fn signers_name(self) -> &'a str {
384 self.0.get_str(DnsRrKey::SIG_SIGNERS_NAME).unwrap_or("")
385 }
386
387 pub fn signature(self) -> &'a [u8] {
389 self.0.get_bin(DnsRrKey::SIG_SIGNATURE).unwrap_or(&[])
390 }
391}
392
393impl fmt::Debug for SigRecord<'_> {
394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 f.debug_struct("SigRecord")
396 .field("name", &self.name())
397 .field("ttl", &self.ttl())
398 .field("type_covered", &self.type_covered())
399 .field("algorithm", &self.algorithm())
400 .field("labels", &self.labels())
401 .field("original_ttl", &self.original_ttl())
402 .field("expiration", &self.expiration())
403 .field("inception", &self.inception())
404 .field("key_tag", &self.key_tag())
405 .field("signers_name", &self.signers_name())
406 .field("signature_len", &self.signature().len())
407 .finish()
408 }
409}
410
411#[derive(Copy, Clone)]
413pub struct SrvRecord<'a>(&'a DnsRr);
414
415impl<'a> SrvRecord<'a> {
416 common_accessors!(SrvRecord);
417
418 pub fn priority(self) -> u16 {
420 self.0.get_u16(DnsRrKey::SRV_PRIORITY)
421 }
422
423 pub fn weight(self) -> u16 {
425 self.0.get_u16(DnsRrKey::SRV_WEIGHT)
426 }
427
428 pub fn port(self) -> u16 {
430 self.0.get_u16(DnsRrKey::SRV_PORT)
431 }
432
433 pub fn target(self) -> &'a str {
435 self.0.get_str(DnsRrKey::SRV_TARGET).unwrap_or("")
436 }
437}
438
439impl fmt::Debug for SrvRecord<'_> {
440 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441 f.debug_struct("SrvRecord")
442 .field("name", &self.name())
443 .field("ttl", &self.ttl())
444 .field("priority", &self.priority())
445 .field("weight", &self.weight())
446 .field("port", &self.port())
447 .field("target", &self.target())
448 .finish()
449 }
450}
451
452#[derive(Copy, Clone)]
454pub struct NaptrRecord<'a>(&'a DnsRr);
455
456impl<'a> NaptrRecord<'a> {
457 common_accessors!(NaptrRecord);
458
459 pub fn order(self) -> u16 {
461 self.0.get_u16(DnsRrKey::NAPTR_ORDER)
462 }
463
464 pub fn preference(self) -> u16 {
466 self.0.get_u16(DnsRrKey::NAPTR_PREFERENCE)
467 }
468
469 pub fn flags(self) -> &'a str {
471 self.0.get_str(DnsRrKey::NAPTR_FLAGS).unwrap_or("")
472 }
473
474 pub fn services(self) -> &'a str {
476 self.0.get_str(DnsRrKey::NAPTR_SERVICES).unwrap_or("")
477 }
478
479 pub fn regexp(self) -> &'a str {
481 self.0.get_str(DnsRrKey::NAPTR_REGEXP).unwrap_or("")
482 }
483
484 pub fn replacement(self) -> &'a str {
486 self.0.get_str(DnsRrKey::NAPTR_REPLACEMENT).unwrap_or("")
487 }
488}
489
490impl fmt::Debug for NaptrRecord<'_> {
491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492 f.debug_struct("NaptrRecord")
493 .field("name", &self.name())
494 .field("ttl", &self.ttl())
495 .field("order", &self.order())
496 .field("preference", &self.preference())
497 .field("flags", &self.flags())
498 .field("services", &self.services())
499 .field("regexp", &self.regexp())
500 .field("replacement", &self.replacement())
501 .finish()
502 }
503}
504
505#[derive(Copy, Clone)]
507pub struct TlsaRecord<'a>(&'a DnsRr);
508
509impl<'a> TlsaRecord<'a> {
510 common_accessors!(TlsaRecord);
511
512 pub fn cert_usage(self) -> u8 {
514 self.0.get_u8(DnsRrKey::TLSA_CERT_USAGE)
515 }
516
517 pub fn selector(self) -> u8 {
519 self.0.get_u8(DnsRrKey::TLSA_SELECTOR)
520 }
521
522 pub fn matching_type(self) -> u8 {
524 self.0.get_u8(DnsRrKey::TLSA_MATCH)
525 }
526
527 pub fn data(self) -> &'a [u8] {
529 self.0.get_bin(DnsRrKey::TLSA_DATA).unwrap_or(&[])
530 }
531}
532
533impl fmt::Debug for TlsaRecord<'_> {
534 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
535 f.debug_struct("TlsaRecord")
536 .field("name", &self.name())
537 .field("ttl", &self.ttl())
538 .field("cert_usage", &self.cert_usage())
539 .field("selector", &self.selector())
540 .field("matching_type", &self.matching_type())
541 .field("data_len", &self.data().len())
542 .finish()
543 }
544}
545
546#[derive(Copy, Clone)]
548pub struct UriRecord<'a>(&'a DnsRr);
549
550impl<'a> UriRecord<'a> {
551 common_accessors!(UriRecord);
552
553 pub fn priority(self) -> u16 {
555 self.0.get_u16(DnsRrKey::URI_PRIORITY)
556 }
557
558 pub fn weight(self) -> u16 {
560 self.0.get_u16(DnsRrKey::URI_WEIGHT)
561 }
562
563 pub fn target(self) -> &'a str {
565 self.0.get_str(DnsRrKey::URI_TARGET).unwrap_or("")
566 }
567}
568
569impl fmt::Debug for UriRecord<'_> {
570 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
571 f.debug_struct("UriRecord")
572 .field("name", &self.name())
573 .field("ttl", &self.ttl())
574 .field("priority", &self.priority())
575 .field("weight", &self.weight())
576 .field("target", &self.target())
577 .finish()
578 }
579}
580
581#[derive(Copy, Clone)]
590pub struct TxtRecord<'a>(&'a DnsRr);
591
592impl<'a> TxtRecord<'a> {
593 common_accessors!(TxtRecord);
594
595 pub fn entry_count(self) -> usize {
598 self.0.get_abin_count(DnsRrKey::TXT_DATA)
599 }
600
601 pub fn entries(self) -> impl Iterator<Item = &'a [u8]> {
604 self.0.abins(DnsRrKey::TXT_DATA)
605 }
606}
607
608impl fmt::Debug for TxtRecord<'_> {
609 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610 f.debug_struct("TxtRecord")
611 .field("name", &self.name())
612 .field("ttl", &self.ttl())
613 .field("entry_count", &self.entry_count())
614 .finish()
615 }
616}
617
618#[derive(Copy, Clone)]
624pub struct OptRecord<'a>(&'a DnsRr);
625
626impl<'a> OptRecord<'a> {
627 common_accessors!(OptRecord);
628
629 pub fn udp_size(self) -> u16 {
632 self.0.get_u16(DnsRrKey::OPT_UDP_SIZE)
633 }
634
635 pub fn version(self) -> u8 {
637 self.0.get_u8(DnsRrKey::OPT_VERSION)
638 }
639
640 pub fn flags(self) -> u16 {
642 self.0.get_u16(DnsRrKey::OPT_FLAGS)
643 }
644
645 pub fn option_count(self) -> usize {
648 self.0.get_opt_count(DnsRrKey::OPT_OPTIONS)
649 }
650
651 pub fn options(self) -> impl Iterator<Item = (u16, Result<OptValue, OptParseError>)> + 'a {
660 self.0
661 .opts(DnsRrKey::OPT_OPTIONS)
662 .map(|(k, v)| (k, parse_opt_value(DnsRrKey::OPT_OPTIONS, k, v)))
663 }
664
665 pub fn raw_options(self) -> impl Iterator<Item = (u16, &'a [u8])> {
669 self.0.opts(DnsRrKey::OPT_OPTIONS)
670 }
671}
672
673impl fmt::Debug for OptRecord<'_> {
674 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
675 f.debug_struct("OptRecord")
676 .field("udp_size", &self.udp_size())
677 .field("version", &self.version())
678 .field("flags", &self.flags())
679 .field("option_count", &self.option_count())
680 .finish()
681 }
682}
683
684#[derive(Copy, Clone)]
686pub struct SvcbRecord<'a>(&'a DnsRr);
687
688impl<'a> SvcbRecord<'a> {
689 common_accessors!(SvcbRecord);
690
691 pub fn priority(self) -> u16 {
693 self.0.get_u16(DnsRrKey::SVCB_PRIORITY)
694 }
695
696 pub fn target(self) -> &'a str {
698 self.0.get_str(DnsRrKey::SVCB_TARGET).unwrap_or("")
699 }
700
701 pub fn param_count(self) -> usize {
704 self.0.get_opt_count(DnsRrKey::SVCB_PARAMS)
705 }
706
707 pub fn params(self) -> impl Iterator<Item = (u16, Result<OptValue, OptParseError>)> + 'a {
716 self.0
717 .opts(DnsRrKey::SVCB_PARAMS)
718 .map(|(k, v)| (k, parse_opt_value(DnsRrKey::SVCB_PARAMS, k, v)))
719 }
720
721 pub fn raw_params(self) -> impl Iterator<Item = (u16, &'a [u8])> {
725 self.0.opts(DnsRrKey::SVCB_PARAMS)
726 }
727}
728
729impl fmt::Debug for SvcbRecord<'_> {
730 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
731 f.debug_struct("SvcbRecord")
732 .field("name", &self.name())
733 .field("ttl", &self.ttl())
734 .field("priority", &self.priority())
735 .field("target", &self.target())
736 .field("param_count", &self.param_count())
737 .finish()
738 }
739}
740
741#[derive(Copy, Clone)]
743pub struct HttpsRecord<'a>(&'a DnsRr);
744
745impl<'a> HttpsRecord<'a> {
746 common_accessors!(HttpsRecord);
747
748 pub fn priority(self) -> u16 {
750 self.0.get_u16(DnsRrKey::HTTPS_PRIORITY)
751 }
752
753 pub fn target(self) -> &'a str {
755 self.0.get_str(DnsRrKey::HTTPS_TARGET).unwrap_or("")
756 }
757
758 pub fn param_count(self) -> usize {
761 self.0.get_opt_count(DnsRrKey::HTTPS_PARAMS)
762 }
763
764 pub fn params(self) -> impl Iterator<Item = (u16, Result<OptValue, OptParseError>)> + 'a {
773 self.0
774 .opts(DnsRrKey::HTTPS_PARAMS)
775 .map(|(k, v)| (k, parse_opt_value(DnsRrKey::HTTPS_PARAMS, k, v)))
776 }
777
778 pub fn raw_params(self) -> impl Iterator<Item = (u16, &'a [u8])> {
782 self.0.opts(DnsRrKey::HTTPS_PARAMS)
783 }
784}
785
786impl fmt::Debug for HttpsRecord<'_> {
787 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
788 f.debug_struct("HttpsRecord")
789 .field("name", &self.name())
790 .field("ttl", &self.ttl())
791 .field("priority", &self.priority())
792 .field("target", &self.target())
793 .field("param_count", &self.param_count())
794 .finish()
795 }
796}
797
798#[derive(Copy, Clone)]
800pub struct CaaRecord<'a>(&'a DnsRr);
801
802impl<'a> CaaRecord<'a> {
803 common_accessors!(CaaRecord);
804
805 pub fn flags(self) -> u8 {
810 self.0.get_u8(DnsRrKey::CAA_CRITICAL)
811 }
812
813 pub fn is_critical(self) -> bool {
816 (self.flags() & 0x80) != 0
817 }
818
819 pub fn tag(self) -> &'a str {
821 self.0.get_str(DnsRrKey::CAA_TAG).unwrap_or("")
822 }
823
824 pub fn value(self) -> &'a [u8] {
829 self.0.get_bin(DnsRrKey::CAA_VALUE).unwrap_or(&[])
830 }
831}
832
833impl fmt::Debug for CaaRecord<'_> {
834 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
835 f.debug_struct("CaaRecord")
836 .field("name", &self.name())
837 .field("ttl", &self.ttl())
838 .field("flags", &self.flags())
839 .field("is_critical", &self.is_critical())
840 .field("tag", &self.tag())
841 .field("value_len", &self.value().len())
842 .finish()
843 }
844}
845
846#[derive(Copy, Clone)]
853pub struct RawRrRecord<'a>(&'a DnsRr);
854
855impl<'a> RawRrRecord<'a> {
856 common_accessors!(RawRrRecord);
857
858 pub fn raw_type(self) -> u16 {
864 self.0.get_u16(DnsRrKey::RAW_RR_TYPE)
865 }
866
867 pub fn data(self) -> &'a [u8] {
872 self.0.get_bin(DnsRrKey::RAW_RR_DATA).unwrap_or(&[])
873 }
874}
875
876impl fmt::Debug for RawRrRecord<'_> {
877 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
878 f.debug_struct("RawRrRecord")
879 .field("name", &self.name())
880 .field("ttl", &self.ttl())
881 .field("raw_type", &self.raw_type())
882 .field("data_len", &self.data().len())
883 .finish()
884 }
885}
886
887#[derive(Copy, Clone, Debug)]
896#[non_exhaustive]
897pub enum TypedRr<'a> {
898 A(ARecord<'a>),
900 Aaaa(AaaaRecord<'a>),
902 Ns(NsRecord<'a>),
904 Cname(CnameRecord<'a>),
906 Soa(SoaRecord<'a>),
908 Ptr(PtrRecord<'a>),
910 Hinfo(HinfoRecord<'a>),
912 Mx(MxRecord<'a>),
914 Txt(TxtRecord<'a>),
916 Sig(SigRecord<'a>),
918 Srv(SrvRecord<'a>),
920 Naptr(NaptrRecord<'a>),
922 Opt(OptRecord<'a>),
924 Tlsa(TlsaRecord<'a>),
926 Svcb(SvcbRecord<'a>),
928 Https(HttpsRecord<'a>),
930 Uri(UriRecord<'a>),
932 Caa(CaaRecord<'a>),
934 RawRr(RawRrRecord<'a>),
936 Any(&'a DnsRr),
939 Unknown(&'a DnsRr),
941}
942
943impl DnsRr {
948 pub fn as_a(&self) -> Option<ARecord<'_>> {
951 (self.rr_type() == DnsRecordType::A).then(|| ARecord::new(self))
952 }
953
954 pub fn as_aaaa(&self) -> Option<AaaaRecord<'_>> {
957 (self.rr_type() == DnsRecordType::AAAA).then(|| AaaaRecord::new(self))
958 }
959
960 pub fn as_ns(&self) -> Option<NsRecord<'_>> {
963 (self.rr_type() == DnsRecordType::NS).then(|| NsRecord::new(self))
964 }
965
966 pub fn as_cname(&self) -> Option<CnameRecord<'_>> {
969 (self.rr_type() == DnsRecordType::CNAME).then(|| CnameRecord::new(self))
970 }
971
972 pub fn as_soa(&self) -> Option<SoaRecord<'_>> {
975 (self.rr_type() == DnsRecordType::SOA).then(|| SoaRecord::new(self))
976 }
977
978 pub fn as_ptr_rr(&self) -> Option<PtrRecord<'_>> {
981 (self.rr_type() == DnsRecordType::PTR).then(|| PtrRecord::new(self))
982 }
983
984 pub fn as_hinfo(&self) -> Option<HinfoRecord<'_>> {
987 (self.rr_type() == DnsRecordType::HINFO).then(|| HinfoRecord::new(self))
988 }
989
990 pub fn as_mx(&self) -> Option<MxRecord<'_>> {
993 (self.rr_type() == DnsRecordType::MX).then(|| MxRecord::new(self))
994 }
995
996 pub fn as_txt(&self) -> Option<TxtRecord<'_>> {
999 (self.rr_type() == DnsRecordType::TXT).then(|| TxtRecord::new(self))
1000 }
1001
1002 pub fn as_sig(&self) -> Option<SigRecord<'_>> {
1005 (self.rr_type() == DnsRecordType::SIG).then(|| SigRecord::new(self))
1006 }
1007
1008 pub fn as_srv(&self) -> Option<SrvRecord<'_>> {
1011 (self.rr_type() == DnsRecordType::SRV).then(|| SrvRecord::new(self))
1012 }
1013
1014 pub fn as_naptr(&self) -> Option<NaptrRecord<'_>> {
1017 (self.rr_type() == DnsRecordType::NAPTR).then(|| NaptrRecord::new(self))
1018 }
1019
1020 pub fn as_opt(&self) -> Option<OptRecord<'_>> {
1023 (self.rr_type() == DnsRecordType::OPT).then(|| OptRecord::new(self))
1024 }
1025
1026 pub fn as_tlsa(&self) -> Option<TlsaRecord<'_>> {
1029 (self.rr_type() == DnsRecordType::TLSA).then(|| TlsaRecord::new(self))
1030 }
1031
1032 pub fn as_svcb(&self) -> Option<SvcbRecord<'_>> {
1035 (self.rr_type() == DnsRecordType::SVCB).then(|| SvcbRecord::new(self))
1036 }
1037
1038 pub fn as_https(&self) -> Option<HttpsRecord<'_>> {
1041 (self.rr_type() == DnsRecordType::HTTPS).then(|| HttpsRecord::new(self))
1042 }
1043
1044 pub fn as_uri(&self) -> Option<UriRecord<'_>> {
1047 (self.rr_type() == DnsRecordType::URI).then(|| UriRecord::new(self))
1048 }
1049
1050 pub fn as_caa(&self) -> Option<CaaRecord<'_>> {
1053 (self.rr_type() == DnsRecordType::CAA).then(|| CaaRecord::new(self))
1054 }
1055
1056 pub fn as_raw_rr(&self) -> Option<RawRrRecord<'_>> {
1059 (self.rr_type() == DnsRecordType::RAW_RR).then(|| RawRrRecord::new(self))
1060 }
1061
1062 pub fn as_typed(&self) -> TypedRr<'_> {
1065 match self.rr_type() {
1066 DnsRecordType::A => TypedRr::A(ARecord::new(self)),
1067 DnsRecordType::AAAA => TypedRr::Aaaa(AaaaRecord::new(self)),
1068 DnsRecordType::NS => TypedRr::Ns(NsRecord::new(self)),
1069 DnsRecordType::CNAME => TypedRr::Cname(CnameRecord::new(self)),
1070 DnsRecordType::SOA => TypedRr::Soa(SoaRecord::new(self)),
1071 DnsRecordType::PTR => TypedRr::Ptr(PtrRecord::new(self)),
1072 DnsRecordType::HINFO => TypedRr::Hinfo(HinfoRecord::new(self)),
1073 DnsRecordType::MX => TypedRr::Mx(MxRecord::new(self)),
1074 DnsRecordType::TXT => TypedRr::Txt(TxtRecord::new(self)),
1075 DnsRecordType::SIG => TypedRr::Sig(SigRecord::new(self)),
1076 DnsRecordType::SRV => TypedRr::Srv(SrvRecord::new(self)),
1077 DnsRecordType::NAPTR => TypedRr::Naptr(NaptrRecord::new(self)),
1078 DnsRecordType::OPT => TypedRr::Opt(OptRecord::new(self)),
1079 DnsRecordType::TLSA => TypedRr::Tlsa(TlsaRecord::new(self)),
1080 DnsRecordType::SVCB => TypedRr::Svcb(SvcbRecord::new(self)),
1081 DnsRecordType::HTTPS => TypedRr::Https(HttpsRecord::new(self)),
1082 DnsRecordType::URI => TypedRr::Uri(UriRecord::new(self)),
1083 DnsRecordType::CAA => TypedRr::Caa(CaaRecord::new(self)),
1084 DnsRecordType::RAW_RR => TypedRr::RawRr(RawRrRecord::new(self)),
1085 DnsRecordType::ANY => TypedRr::Any(self),
1086 DnsRecordType::UNKNOWN(_) => TypedRr::Unknown(self),
1087 }
1088 }
1089}
1090
1091#[cfg(test)]
1092mod tests {
1093 use super::*;
1094 use crate::dns::{DnsFlags, DnsOpcode, DnsParseFlags, DnsRcode, DnsRecord, DnsSection};
1095
1096 fn assert_send<T: Send>() {}
1097 fn assert_sync<T: Sync>() {}
1098 fn assert_copy<T: Copy>() {}
1099
1100 #[test]
1101 fn wrappers_are_send_sync_copy() {
1102 assert_send::<ARecord<'_>>();
1103 assert_sync::<ARecord<'_>>();
1104 assert_copy::<ARecord<'_>>();
1105 assert_send::<TypedRr<'_>>();
1106 assert_sync::<TypedRr<'_>>();
1107 assert_copy::<TypedRr<'_>>();
1108 }
1109
1110 fn make_rec(rtype: DnsRecordType) -> DnsRecord {
1111 let mut rec =
1112 DnsRecord::new(0, DnsFlags::QR, DnsOpcode::Query, DnsRcode::NoError).expect("create");
1113 rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
1117 .expect("query_add");
1118 rec.rr_add(DnsSection::Answer, "example.com", rtype, DnsCls::IN, 300)
1119 .expect("rr_add");
1120 rec
1121 }
1122
1123 fn first_rr(rec: &DnsRecord) -> &DnsRr {
1124 rec.rr(DnsSection::Answer, 0).expect("rr")
1125 }
1126
1127 #[test]
1130 fn as_a_matches_only_a() {
1131 let rec = make_rec(DnsRecordType::A);
1132 let rr = first_rr(&rec);
1133 assert!(rr.as_a().is_some());
1134 assert!(rr.as_aaaa().is_none());
1135 assert!(rr.as_mx().is_none());
1136 assert!(rr.as_txt().is_none());
1137 }
1138
1139 #[test]
1140 fn as_typed_dispatches_correctly() {
1141 for &rtype in &[
1142 DnsRecordType::A,
1143 DnsRecordType::AAAA,
1144 DnsRecordType::MX,
1145 DnsRecordType::TXT,
1146 DnsRecordType::CAA,
1147 DnsRecordType::SRV,
1148 ] {
1149 let rec = make_rec(rtype);
1150 let rr = first_rr(&rec);
1151 match (rtype, rr.as_typed()) {
1152 (DnsRecordType::A, TypedRr::A(_))
1153 | (DnsRecordType::AAAA, TypedRr::Aaaa(_))
1154 | (DnsRecordType::MX, TypedRr::Mx(_))
1155 | (DnsRecordType::TXT, TypedRr::Txt(_))
1156 | (DnsRecordType::CAA, TypedRr::Caa(_))
1157 | (DnsRecordType::SRV, TypedRr::Srv(_)) => {}
1158 (rt, other) => panic!("type {rt:?} dispatched to wrong variant: {other:?}"),
1159 }
1160 }
1161 }
1162
1163 #[test]
1166 fn builder_partial_fields_have_defaults() {
1167 let rec = make_rec(DnsRecordType::A);
1169 let a = first_rr(&rec).as_a().expect("as_a");
1170 let _ = a.addr();
1171 assert_eq!(a.ttl(), 300);
1172
1173 let rec = make_rec(DnsRecordType::MX);
1175 let mx = first_rr(&rec).as_mx().expect("as_mx");
1176 assert_eq!(mx.preference(), 0);
1177 assert_eq!(mx.exchange(), "");
1178
1179 let rec = make_rec(DnsRecordType::SOA);
1181 let soa = first_rr(&rec).as_soa().expect("as_soa");
1182 assert_eq!(soa.mname(), "");
1183 assert_eq!(soa.rname(), "");
1184 assert_eq!(soa.serial(), 0);
1185 assert_eq!(soa.minimum(), 0);
1186
1187 let rec = make_rec(DnsRecordType::CAA);
1189 let caa = first_rr(&rec).as_caa().expect("as_caa");
1190 assert_eq!(caa.flags(), 0);
1191 assert!(!caa.is_critical());
1192 assert_eq!(caa.tag(), "");
1193 assert_eq!(caa.value(), &[] as &[u8]);
1194
1195 let rec = make_rec(DnsRecordType::TXT);
1197 let txt = first_rr(&rec).as_txt().expect("as_txt");
1198 assert_eq!(txt.entry_count(), 0);
1199 assert_eq!(txt.entries().count(), 0);
1200
1201 let rec = make_rec(DnsRecordType::RAW_RR);
1203 let raw = first_rr(&rec).as_raw_rr().expect("as_raw_rr");
1204 assert_eq!(raw.raw_type(), 0);
1205 assert_eq!(raw.data(), &[] as &[u8]);
1206 }
1207
1208 #[test]
1211 fn a_full_roundtrip() {
1212 let mut rec = make_rec(DnsRecordType::A);
1213 let rr = rec.rr_mut(DnsSection::Answer, 0).expect("rr_mut");
1214 rr.set_addr(DnsRrKey::A_ADDR, Ipv4Addr::new(10, 0, 0, 1))
1215 .expect("set");
1216 let a = first_rr(&rec).as_a().expect("as_a");
1217 assert_eq!(a.addr(), Ipv4Addr::new(10, 0, 0, 1));
1218 assert_eq!(a.name(), "example.com");
1219 }
1220
1221 #[test]
1222 fn aaaa_full_roundtrip() {
1223 let mut rec = make_rec(DnsRecordType::AAAA);
1224 let addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
1225 let rr = rec.rr_mut(DnsSection::Answer, 0).expect("rr_mut");
1226 rr.set_addr6(DnsRrKey::AAAA_ADDR, addr).expect("set");
1227 let aaaa = first_rr(&rec).as_aaaa().expect("as_aaaa");
1228 assert_eq!(aaaa.addr(), addr);
1229 }
1230
1231 #[test]
1232 fn mx_full_roundtrip() {
1233 let mut rec = make_rec(DnsRecordType::MX);
1234 let rr = rec.rr_mut(DnsSection::Answer, 0).expect("rr_mut");
1235 rr.set_u16(DnsRrKey::MX_PREFERENCE, 10).expect("pref");
1236 rr.set_str(DnsRrKey::MX_EXCHANGE, "mail.example.com")
1237 .expect("exch");
1238 let mx = first_rr(&rec).as_mx().expect("as_mx");
1239 assert_eq!(mx.preference(), 10);
1240 assert_eq!(mx.exchange(), "mail.example.com");
1241 }
1242
1243 #[test]
1244 fn caa_critical_bit() {
1245 let mut rec = make_rec(DnsRecordType::CAA);
1246 let rr = rec.rr_mut(DnsSection::Answer, 0).expect("rr_mut");
1247 rr.set_u8(DnsRrKey::CAA_CRITICAL, 0x80).expect("crit");
1248 rr.set_str(DnsRrKey::CAA_TAG, "issue").expect("tag");
1249 rr.set_bin(DnsRrKey::CAA_VALUE, b"ca.example.net")
1250 .expect("val");
1251 let caa = first_rr(&rec).as_caa().expect("as_caa");
1252 assert_eq!(caa.flags(), 0x80);
1253 assert!(caa.is_critical());
1254 assert_eq!(caa.tag(), "issue");
1255 assert_eq!(caa.value(), &b"ca.example.net"[..]);
1256 }
1257
1258 #[test]
1259 fn txt_entries_iterator() {
1260 let mut rec = make_rec(DnsRecordType::TXT);
1261 let rr = rec.rr_mut(DnsSection::Answer, 0).expect("rr_mut");
1262 rr.add_abin(DnsRrKey::TXT_DATA, b"first").expect("a1");
1263 rr.add_abin(DnsRrKey::TXT_DATA, b"second").expect("a2");
1264 let txt = first_rr(&rec).as_txt().expect("as_txt");
1265 assert_eq!(txt.entry_count(), 2);
1266 let entries: Vec<&[u8]> = txt.entries().collect();
1267 assert_eq!(entries, vec![&b"first"[..], &b"second"[..]]);
1268 }
1269
1270 fn build_then_parse(rec: &DnsRecord) -> DnsRecord {
1273 let wire = rec.write().expect("write");
1274 DnsRecord::parse(&wire, DnsParseFlags::empty()).expect("parse")
1275 }
1276
1277 #[test]
1278 fn parsed_a_record() {
1279 let mut rec = make_rec(DnsRecordType::A);
1280 rec.rr_mut(DnsSection::Answer, 0)
1281 .unwrap()
1282 .set_addr(DnsRrKey::A_ADDR, Ipv4Addr::new(93, 184, 216, 34))
1283 .unwrap();
1284 let parsed = build_then_parse(&rec);
1285 let a = first_rr(&parsed).as_a().expect("as_a");
1286 assert_eq!(a.addr(), Ipv4Addr::new(93, 184, 216, 34));
1287 }
1288
1289 #[test]
1290 fn parsed_soa_record() {
1291 let mut rec = make_rec(DnsRecordType::SOA);
1292 let rr = rec.rr_mut(DnsSection::Answer, 0).unwrap();
1293 rr.set_str(DnsRrKey::SOA_MNAME, "ns1.example.com").unwrap();
1294 rr.set_str(DnsRrKey::SOA_RNAME, "hostmaster.example.com")
1295 .unwrap();
1296 rr.set_u32(DnsRrKey::SOA_SERIAL, 42).unwrap();
1297 rr.set_u32(DnsRrKey::SOA_REFRESH, 3600).unwrap();
1298 rr.set_u32(DnsRrKey::SOA_RETRY, 900).unwrap();
1299 rr.set_u32(DnsRrKey::SOA_EXPIRE, 604800).unwrap();
1300 rr.set_u32(DnsRrKey::SOA_MINIMUM, 60).unwrap();
1301 let parsed = build_then_parse(&rec);
1302 let soa = first_rr(&parsed).as_soa().expect("as_soa");
1303 assert_eq!(soa.mname(), "ns1.example.com");
1304 assert_eq!(soa.rname(), "hostmaster.example.com");
1305 assert_eq!(soa.serial(), 42);
1306 assert_eq!(soa.expire(), 604800);
1307 }
1308
1309 #[test]
1310 fn parsed_txt_record() {
1311 let mut rec = make_rec(DnsRecordType::TXT);
1312 rec.rr_mut(DnsSection::Answer, 0)
1313 .unwrap()
1314 .add_abin(DnsRrKey::TXT_DATA, b"v=spf1 -all")
1315 .unwrap();
1316 let parsed = build_then_parse(&rec);
1317 let txt = first_rr(&parsed).as_txt().expect("as_txt");
1318 assert_eq!(txt.entry_count(), 1);
1319 assert_eq!(txt.entries().next(), Some(&b"v=spf1 -all"[..]));
1320 }
1321
1322 #[test]
1323 fn parsed_mx_record() {
1324 let mut rec = make_rec(DnsRecordType::MX);
1325 let rr = rec.rr_mut(DnsSection::Answer, 0).unwrap();
1326 rr.set_u16(DnsRrKey::MX_PREFERENCE, 5).unwrap();
1327 rr.set_str(DnsRrKey::MX_EXCHANGE, "mx.example.com").unwrap();
1328 let parsed = build_then_parse(&rec);
1329 let mx = first_rr(&parsed).as_mx().expect("as_mx");
1330 assert_eq!(mx.preference(), 5);
1331 assert_eq!(mx.exchange(), "mx.example.com");
1332 }
1333
1334 fn build<F: FnOnce(&mut DnsRr)>(rtype: DnsRecordType, populate: F) -> DnsRecord {
1339 let mut rec = make_rec(rtype);
1340 populate(rec.rr_mut(DnsSection::Answer, 0).expect("rr_mut"));
1341 rec
1342 }
1343
1344 #[test]
1345 fn ns_full_accessors_and_debug() {
1346 let rec = build(DnsRecordType::NS, |rr| {
1347 rr.set_str(DnsRrKey::NS_NSDNAME, "ns1.example.com").unwrap();
1348 });
1349 let ns = first_rr(&rec).as_ns().expect("as_ns");
1350 assert_eq!(ns.nsdname(), "ns1.example.com");
1351 assert_eq!(ns.name(), "example.com");
1352 assert_eq!(ns.dns_class(), DnsCls::IN);
1353 assert_eq!(ns.ttl(), 300);
1354 let _ = ns.as_dns_rr();
1355 assert!(format!("{ns:?}").contains("NsRecord"));
1356 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::Ns(_)));
1357 }
1358
1359 #[test]
1360 fn cname_full_accessors_and_debug() {
1361 let rec = build(DnsRecordType::CNAME, |rr| {
1362 rr.set_str(DnsRrKey::CNAME_CNAME, "alias.example.com")
1363 .unwrap();
1364 });
1365 let c = first_rr(&rec).as_cname().expect("as_cname");
1366 assert_eq!(c.cname(), "alias.example.com");
1367 assert!(format!("{c:?}").contains("CnameRecord"));
1368 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::Cname(_)));
1369 }
1370
1371 #[test]
1372 fn ptr_full_accessors_and_debug() {
1373 let rec = build(DnsRecordType::PTR, |rr| {
1374 rr.set_str(DnsRrKey::PTR_DNAME, "host.example.com").unwrap();
1375 });
1376 let p = first_rr(&rec).as_ptr_rr().expect("as_ptr_rr");
1377 assert_eq!(p.dname(), "host.example.com");
1378 assert!(format!("{p:?}").contains("PtrRecord"));
1379 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::Ptr(_)));
1380 }
1381
1382 #[test]
1383 fn hinfo_full_accessors_and_debug() {
1384 let rec = build(DnsRecordType::HINFO, |rr| {
1385 rr.set_str(DnsRrKey::HINFO_CPU, "x86_64").unwrap();
1386 rr.set_str(DnsRrKey::HINFO_OS, "linux").unwrap();
1387 });
1388 let h = first_rr(&rec).as_hinfo().expect("as_hinfo");
1389 assert_eq!(h.cpu(), "x86_64");
1390 assert_eq!(h.os(), "linux");
1391 assert!(format!("{h:?}").contains("HinfoRecord"));
1392 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::Hinfo(_)));
1393 }
1394
1395 #[test]
1396 fn soa_full_accessors_and_debug() {
1397 let rec = build(DnsRecordType::SOA, |rr| {
1398 rr.set_str(DnsRrKey::SOA_MNAME, "ns.example.com").unwrap();
1399 rr.set_str(DnsRrKey::SOA_RNAME, "hostmaster.example.com")
1400 .unwrap();
1401 rr.set_u32(DnsRrKey::SOA_SERIAL, 1).unwrap();
1402 rr.set_u32(DnsRrKey::SOA_REFRESH, 2).unwrap();
1403 rr.set_u32(DnsRrKey::SOA_RETRY, 3).unwrap();
1404 rr.set_u32(DnsRrKey::SOA_EXPIRE, 4).unwrap();
1405 rr.set_u32(DnsRrKey::SOA_MINIMUM, 5).unwrap();
1406 });
1407 let s = first_rr(&rec).as_soa().expect("as_soa");
1408 assert_eq!(s.mname(), "ns.example.com");
1409 assert_eq!(s.rname(), "hostmaster.example.com");
1410 assert_eq!(s.serial(), 1);
1411 assert_eq!(s.refresh(), 2);
1412 assert_eq!(s.retry(), 3);
1413 assert_eq!(s.expire(), 4);
1414 assert_eq!(s.minimum(), 5);
1415 assert!(format!("{s:?}").contains("SoaRecord"));
1416 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::Soa(_)));
1417 }
1418
1419 #[test]
1420 fn sig_full_accessors_and_debug() {
1421 let rec = build(DnsRecordType::SIG, |rr| {
1422 rr.set_u16(DnsRrKey::SIG_TYPE_COVERED, 1).unwrap();
1423 rr.set_u8(DnsRrKey::SIG_ALGORITHM, 8).unwrap();
1424 rr.set_u8(DnsRrKey::SIG_LABELS, 2).unwrap();
1425 rr.set_u32(DnsRrKey::SIG_ORIGINAL_TTL, 3600).unwrap();
1426 rr.set_u32(DnsRrKey::SIG_EXPIRATION, 4_000_000_000).unwrap();
1427 rr.set_u32(DnsRrKey::SIG_INCEPTION, 1_000_000_000).unwrap();
1428 rr.set_u16(DnsRrKey::SIG_KEY_TAG, 12345).unwrap();
1429 rr.set_str(DnsRrKey::SIG_SIGNERS_NAME, "example.com")
1430 .unwrap();
1431 rr.set_bin(DnsRrKey::SIG_SIGNATURE, b"\x01\x02\x03\x04")
1432 .unwrap();
1433 });
1434 let s = first_rr(&rec).as_sig().expect("as_sig");
1435 assert_eq!(s.type_covered(), 1);
1436 assert_eq!(s.algorithm(), 8);
1437 assert_eq!(s.labels(), 2);
1438 assert_eq!(s.original_ttl(), 3600);
1439 assert_eq!(s.expiration(), 4_000_000_000);
1440 assert_eq!(s.inception(), 1_000_000_000);
1441 assert_eq!(s.key_tag(), 12345);
1442 assert_eq!(s.signers_name(), "example.com");
1443 assert_eq!(s.signature(), &b"\x01\x02\x03\x04"[..]);
1444 assert!(format!("{s:?}").contains("SigRecord"));
1445 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::Sig(_)));
1446 }
1447
1448 #[test]
1449 fn srv_full_accessors_and_debug() {
1450 let rec = build(DnsRecordType::SRV, |rr| {
1451 rr.set_u16(DnsRrKey::SRV_PRIORITY, 10).unwrap();
1452 rr.set_u16(DnsRrKey::SRV_WEIGHT, 20).unwrap();
1453 rr.set_u16(DnsRrKey::SRV_PORT, 443).unwrap();
1454 rr.set_str(DnsRrKey::SRV_TARGET, "svc.example.com").unwrap();
1455 });
1456 let s = first_rr(&rec).as_srv().expect("as_srv");
1457 assert_eq!(s.priority(), 10);
1458 assert_eq!(s.weight(), 20);
1459 assert_eq!(s.port(), 443);
1460 assert_eq!(s.target(), "svc.example.com");
1461 assert!(format!("{s:?}").contains("SrvRecord"));
1462 }
1463
1464 #[test]
1465 fn naptr_full_accessors_and_debug() {
1466 let rec = build(DnsRecordType::NAPTR, |rr| {
1467 rr.set_u16(DnsRrKey::NAPTR_ORDER, 100).unwrap();
1468 rr.set_u16(DnsRrKey::NAPTR_PREFERENCE, 10).unwrap();
1469 rr.set_str(DnsRrKey::NAPTR_FLAGS, "U").unwrap();
1470 rr.set_str(DnsRrKey::NAPTR_SERVICES, "E2U+sip").unwrap();
1471 rr.set_str(DnsRrKey::NAPTR_REGEXP, "!^.*$!sip:info@example.com!")
1472 .unwrap();
1473 rr.set_str(DnsRrKey::NAPTR_REPLACEMENT, ".").unwrap();
1474 });
1475 let n = first_rr(&rec).as_naptr().expect("as_naptr");
1476 assert_eq!(n.order(), 100);
1477 assert_eq!(n.preference(), 10);
1478 assert_eq!(n.flags(), "U");
1479 assert_eq!(n.services(), "E2U+sip");
1480 assert_eq!(n.regexp(), "!^.*$!sip:info@example.com!");
1481 assert_eq!(n.replacement(), ".");
1482 assert!(format!("{n:?}").contains("NaptrRecord"));
1483 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::Naptr(_)));
1484 }
1485
1486 #[test]
1487 fn tlsa_full_accessors_and_debug() {
1488 let rec = build(DnsRecordType::TLSA, |rr| {
1489 rr.set_u8(DnsRrKey::TLSA_CERT_USAGE, 3).unwrap();
1490 rr.set_u8(DnsRrKey::TLSA_SELECTOR, 1).unwrap();
1491 rr.set_u8(DnsRrKey::TLSA_MATCH, 1).unwrap();
1492 rr.set_bin(DnsRrKey::TLSA_DATA, &[0xab; 32]).unwrap();
1493 });
1494 let t = first_rr(&rec).as_tlsa().expect("as_tlsa");
1495 assert_eq!(t.cert_usage(), 3);
1496 assert_eq!(t.selector(), 1);
1497 assert_eq!(t.matching_type(), 1);
1498 assert_eq!(t.data().len(), 32);
1499 assert!(format!("{t:?}").contains("TlsaRecord"));
1500 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::Tlsa(_)));
1501 }
1502
1503 #[test]
1504 fn uri_full_accessors_and_debug() {
1505 let rec = build(DnsRecordType::URI, |rr| {
1506 rr.set_u16(DnsRrKey::URI_PRIORITY, 1).unwrap();
1507 rr.set_u16(DnsRrKey::URI_WEIGHT, 2).unwrap();
1508 rr.set_str(DnsRrKey::URI_TARGET, "https://example.com/")
1509 .unwrap();
1510 });
1511 let u = first_rr(&rec).as_uri().expect("as_uri");
1512 assert_eq!(u.priority(), 1);
1513 assert_eq!(u.weight(), 2);
1514 assert_eq!(u.target(), "https://example.com/");
1515 assert!(format!("{u:?}").contains("UriRecord"));
1516 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::Uri(_)));
1517 }
1518
1519 #[test]
1520 fn svcb_full_accessors_and_debug() {
1521 let rec = build(DnsRecordType::SVCB, |rr| {
1522 rr.set_u16(DnsRrKey::SVCB_PRIORITY, 1).unwrap();
1523 rr.set_str(DnsRrKey::SVCB_TARGET, "svc.example.com")
1524 .unwrap();
1525 rr.set_opt(DnsRrKey::SVCB_PARAMS, 1, b"\x00\x02h2").unwrap();
1527 });
1528 let s = first_rr(&rec).as_svcb().expect("as_svcb");
1529 assert_eq!(s.priority(), 1);
1530 assert_eq!(s.target(), "svc.example.com");
1531 assert_eq!(s.param_count(), 1);
1532 let raw: Vec<(u16, &[u8])> = s.raw_params().collect();
1534 assert_eq!(raw, vec![(1u16, &b"\x00\x02h2"[..])]);
1535 let parsed: Vec<(u16, OptValue)> =
1537 s.params().map(|(k, v)| (k, v.expect("decode"))).collect();
1538 assert_eq!(parsed.len(), 1);
1539 assert_eq!(parsed[0].0, 1);
1540 assert!(matches!(parsed[0].1, OptValue::StrList(_)));
1541 assert!(format!("{s:?}").contains("SvcbRecord"));
1542 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::Svcb(_)));
1543 }
1544
1545 #[test]
1546 fn https_full_accessors_and_debug() {
1547 let rec = build(DnsRecordType::HTTPS, |rr| {
1548 rr.set_u16(DnsRrKey::HTTPS_PRIORITY, 1).unwrap();
1549 rr.set_str(DnsRrKey::HTTPS_TARGET, "svc.example.com")
1550 .unwrap();
1551 rr.set_opt(DnsRrKey::HTTPS_PARAMS, 1, b"\x00\x02h3")
1552 .unwrap();
1553 });
1554 let h = first_rr(&rec).as_https().expect("as_https");
1555 assert_eq!(h.priority(), 1);
1556 assert_eq!(h.target(), "svc.example.com");
1557 assert_eq!(h.param_count(), 1);
1558 assert_eq!(h.raw_params().count(), 1);
1559 assert_eq!(h.params().count(), 1);
1560 assert!(format!("{h:?}").contains("HttpsRecord"));
1561 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::Https(_)));
1562 }
1563
1564 #[test]
1565 fn opt_full_accessors_and_debug() {
1566 let mut rec =
1569 DnsRecord::new(0, DnsFlags::QR, DnsOpcode::Query, DnsRcode::NoError).expect("create");
1570 rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
1571 .expect("query_add");
1572 if rec
1573 .rr_add(
1574 DnsSection::Additional,
1575 "",
1576 DnsRecordType::OPT,
1577 DnsCls::IN,
1578 0,
1579 )
1580 .is_err()
1581 {
1582 return;
1583 }
1584 let rr = rec.rr_mut(DnsSection::Additional, 0).expect("rr_mut");
1585 let _ = rr.set_u16(DnsRrKey::OPT_UDP_SIZE, 4096);
1586 let _ = rr.set_u8(DnsRrKey::OPT_VERSION, 0);
1587 let _ = rr.set_u16(DnsRrKey::OPT_FLAGS, 0x8000);
1588 let _ = rr.set_opt(DnsRrKey::OPT_OPTIONS, 10, b"\x01\x02");
1589 let opt_rr = rec
1590 .rr(DnsSection::Additional, 0)
1591 .expect("rr")
1592 .as_opt()
1593 .expect("as_opt");
1594 let _ = opt_rr.udp_size();
1595 let _ = opt_rr.version();
1596 let _ = opt_rr.flags();
1597 let _ = opt_rr.option_count();
1598 let _ = opt_rr.options().count();
1599 let _ = opt_rr.raw_options().count();
1600 let _ = format!("{opt_rr:?}");
1601 assert!(matches!(
1602 rec.rr(DnsSection::Additional, 0).unwrap().as_typed(),
1603 TypedRr::Opt(_)
1604 ));
1605 }
1606
1607 #[test]
1608 fn raw_rr_full_accessors_and_debug() {
1609 let rec = build(DnsRecordType::RAW_RR, |rr| {
1610 let _ = rr.set_u16(DnsRrKey::RAW_RR_TYPE, 99);
1611 let _ = rr.set_bin(DnsRrKey::RAW_RR_DATA, b"hello");
1612 });
1613 let r = first_rr(&rec).as_raw_rr().expect("as_raw_rr");
1614 let _ = r.raw_type();
1615 let _ = r.data();
1616 assert!(format!("{r:?}").contains("RawRrRecord"));
1617 assert!(matches!(first_rr(&rec).as_typed(), TypedRr::RawRr(_)));
1618 }
1619
1620 #[test]
1621 fn debug_impls_for_all_simple_wrappers() {
1622 let rec = make_rec(DnsRecordType::A);
1625 assert!(format!("{:?}", first_rr(&rec).as_a().unwrap()).contains("ARecord"));
1626 let rec = make_rec(DnsRecordType::AAAA);
1627 assert!(format!("{:?}", first_rr(&rec).as_aaaa().unwrap()).contains("AaaaRecord"));
1628 let rec = make_rec(DnsRecordType::MX);
1629 assert!(format!("{:?}", first_rr(&rec).as_mx().unwrap()).contains("MxRecord"));
1630 let rec = make_rec(DnsRecordType::TXT);
1631 assert!(format!("{:?}", first_rr(&rec).as_txt().unwrap()).contains("TxtRecord"));
1632 let rec = make_rec(DnsRecordType::CAA);
1633 assert!(format!("{:?}", first_rr(&rec).as_caa().unwrap()).contains("CaaRecord"));
1634 assert!(format!("{:?}", first_rr(&rec).as_typed()).contains("Caa"));
1636 }
1637
1638 #[test]
1639 fn as_discriminators_return_none_on_mismatch() {
1640 let rec = make_rec(DnsRecordType::A);
1641 let rr = first_rr(&rec);
1642 assert!(rr.as_ns().is_none());
1643 assert!(rr.as_cname().is_none());
1644 assert!(rr.as_soa().is_none());
1645 assert!(rr.as_ptr_rr().is_none());
1646 assert!(rr.as_hinfo().is_none());
1647 assert!(rr.as_sig().is_none());
1648 assert!(rr.as_srv().is_none());
1649 assert!(rr.as_naptr().is_none());
1650 assert!(rr.as_opt().is_none());
1651 assert!(rr.as_tlsa().is_none());
1652 assert!(rr.as_svcb().is_none());
1653 assert!(rr.as_https().is_none());
1654 assert!(rr.as_uri().is_none());
1655 assert!(rr.as_caa().is_none());
1656 assert!(rr.as_raw_rr().is_none());
1657 }
1658}