Skip to main content

dnsmsg_parser/
dns_message_parser.rs

1use std::{fmt::Write as _, str::Utf8Error};
2
3use data_encoding::{BASE32HEX_NOPAD, BASE64, HEXUPPER};
4use hickory_proto::{
5    ProtoError,
6    dnssec::{
7        PublicKey, SupportedAlgorithms, Verifier,
8        rdata::{CDNSKEY, CDS, DNSKEY, DNSSECRData, DS},
9    },
10    op::{Message as TrustDnsMessage, Query},
11    rr::{
12        Name, RData, Record, RecordType,
13        rdata::{
14            A, AAAA, NULL, OPT, SVCB,
15            opt::{EdnsCode, EdnsOption},
16        },
17    },
18    serialize::binary::{BinDecodable, BinDecoder},
19};
20use snafu::Snafu;
21
22use super::dns_message::{
23    self, DnsQueryMessage, DnsRecord, DnsUpdateMessage, EdnsOptionEntry, OptPseudoSection,
24    QueryHeader, QueryQuestion, UpdateHeader, ZoneInfo,
25};
26use crate::ede::{EDE, EDE_OPTION_CODE};
27
28/// Error type for DNS message parsing
29#[derive(Debug, Snafu)]
30pub enum DnsMessageParserError {
31    #[snafu(display("Encountered error: {}", cause))]
32    SimpleError { cause: String },
33
34    #[snafu(display("Encountered error from TrustDns: {}", source))]
35    TrustDnsError { source: ProtoError },
36
37    #[snafu(display("UTF8Error: {}", source))]
38    Utf8ParsingError { source: Utf8Error },
39}
40
41/// Result alias for parsing
42pub type DnsParserResult<T> = Result<T, DnsMessageParserError>;
43
44/// Options for DNS message parser
45#[derive(Debug, Default, Clone)]
46pub struct DnsParserOptions {
47    /// Whether hostnames in RData should be lowercased, for consistency
48    pub lowercase_hostnames: bool,
49}
50
51trait DnsParserOptionsTarget {
52    fn to_string_with_options(&self, options: &DnsParserOptions) -> String;
53}
54
55impl DnsParserOptionsTarget for Name {
56    fn to_string_with_options(&self, options: &DnsParserOptions) -> String {
57        if options.lowercase_hostnames {
58            self.to_lowercase().to_string()
59        } else {
60            self.to_string()
61        }
62    }
63}
64
65/// A DNS message parser
66#[derive(Debug)]
67pub struct DnsMessageParser {
68    raw_message: Vec<u8>,
69    // This field is used to facilitate parsing of compressed domain names contained
70    // in some record data. We could instantiate a new copy of the raw_message whenever
71    // we need to parse rdata, but to avoid impact on performance, we'll instantiate
72    // only one copy of raw_message upon the first call to parse an rdata that may
73    // contain compressed domain name, and store it here as a member field; for
74    // subsequent invocations of the same call, we simply reuse this copy.
75    raw_message_for_rdata_parsing: Option<Vec<u8>>,
76    options: DnsParserOptions,
77}
78
79impl DnsMessageParser {
80    pub fn new(raw_message: Vec<u8>) -> Self {
81        DnsMessageParser {
82            raw_message,
83            raw_message_for_rdata_parsing: None,
84            options: DnsParserOptions::default(),
85        }
86    }
87
88    pub fn with_options(raw_message: Vec<u8>, options: DnsParserOptions) -> Self {
89        DnsMessageParser {
90            raw_message,
91            raw_message_for_rdata_parsing: None,
92            options,
93        }
94    }
95
96    pub fn raw_message(&self) -> &[u8] {
97        &self.raw_message
98    }
99
100    pub fn parse_as_query_message(&mut self) -> DnsParserResult<DnsQueryMessage> {
101        let msg = TrustDnsMessage::from_vec(&self.raw_message).map_err(|source| {
102            DnsMessageParserError::TrustDnsError {
103                source: ProtoError::from(source),
104            }
105        })?;
106        let header = parse_dns_query_message_header(&msg);
107        let edns_section = parse_edns(&msg).transpose()?;
108        let rcode_high = edns_section.as_ref().map_or(0, |edns| edns.extended_rcode);
109        let response_code = (u16::from(rcode_high) << 4) | ((u16::from(header.rcode)) & 0x000F);
110
111        Ok(DnsQueryMessage {
112            response_code,
113            response: parse_response_code(response_code),
114            header,
115            question_section: self.parse_dns_query_message_question_section(&msg),
116            answer_section: self.parse_dns_message_section(&msg.answers)?,
117            authority_section: self.parse_dns_message_section(&msg.authorities)?,
118            additional_section: self.parse_dns_message_section(&msg.additionals)?,
119            opt_pseudo_section: edns_section,
120        })
121    }
122
123    pub fn parse_as_update_message(&mut self) -> DnsParserResult<DnsUpdateMessage> {
124        let msg = TrustDnsMessage::from_vec(&self.raw_message).map_err(|source| {
125            DnsMessageParserError::TrustDnsError {
126                source: ProtoError::from(source),
127            }
128        })?;
129        let header = parse_dns_update_message_header(&msg);
130        let response_code = (u16::from(header.rcode)) & 0x000F;
131        Ok(DnsUpdateMessage {
132            response_code,
133            response: parse_response_code(response_code),
134            header,
135            zone_to_update: self.parse_dns_update_message_zone_section(&msg)?,
136            prerequisite_section: self.parse_dns_message_section(&msg.answers)?,
137            update_section: self.parse_dns_message_section(&msg.authorities)?,
138            additional_section: self.parse_dns_message_section(&msg.additionals)?,
139        })
140    }
141
142    fn parse_dns_query_message_question_section(
143        &self,
144        dns_message: &TrustDnsMessage,
145    ) -> Vec<QueryQuestion> {
146        dns_message
147            .queries
148            .iter()
149            .map(|query| self.parse_dns_query_question(query))
150            .collect()
151    }
152
153    fn parse_dns_query_question(&self, question: &Query) -> QueryQuestion {
154        QueryQuestion {
155            name: question.name().to_string_with_options(&self.options),
156            class: question.query_class().to_string(),
157            record_type: format_record_type(question.query_type()),
158            record_type_id: u16::from(question.query_type()),
159        }
160    }
161
162    fn parse_dns_update_message_zone_section(
163        &self,
164        dns_message: &TrustDnsMessage,
165    ) -> DnsParserResult<ZoneInfo> {
166        let zones = dns_message
167            .queries
168            .iter()
169            .map(|query| self.parse_dns_query_question(query).into())
170            .collect::<Vec<ZoneInfo>>();
171
172        zones
173            .first()
174            .cloned()
175            .ok_or_else(|| DnsMessageParserError::SimpleError {
176                cause: format!(
177                    "Unexpected number of records in update section: {}",
178                    zones.len()
179                ),
180            })
181    }
182
183    fn parse_dns_message_section(&mut self, records: &[Record]) -> DnsParserResult<Vec<DnsRecord>> {
184        records
185            .iter()
186            .map(|record| self.parse_dns_record(record))
187            .collect::<Result<Vec<_>, _>>()
188    }
189
190    pub(crate) fn parse_dns_record(&mut self, record: &Record) -> DnsParserResult<DnsRecord> {
191        let record_data = match &record.data {
192            RData::Unknown { code, rdata } => self.format_unknown_rdata(u16::from(*code), rdata),
193            RData::Update0(_) => Ok((Some(String::from("")), None)), // Previously none value
194            rdata => self.format_rdata(rdata),
195        }?;
196
197        Ok(DnsRecord {
198            name: record.name.to_string_with_options(&self.options),
199            class: record.dns_class.to_string(),
200            record_type: format_record_type(record.record_type()),
201            record_type_id: u16::from(record.record_type()),
202            ttl: record.ttl,
203            rdata: record_data.0,
204            rdata_bytes: record_data.1,
205        })
206    }
207
208    fn get_rdata_decoder_with_raw_message(&mut self, raw_rdata: &[u8]) -> BinDecoder<'_> {
209        let (index, raw_message_for_rdata_parsing_data) =
210            match self.raw_message_for_rdata_parsing.take() {
211                Some(mut buf) => {
212                    let index = buf.len();
213                    buf.extend_from_slice(raw_rdata);
214                    (index, buf)
215                }
216                None => {
217                    let mut buf = Vec::<u8>::with_capacity(self.raw_message.len() * 2);
218                    buf.extend(&self.raw_message);
219                    buf.extend_from_slice(raw_rdata);
220                    (self.raw_message.len(), buf)
221                }
222            };
223        self.raw_message_for_rdata_parsing = Some(raw_message_for_rdata_parsing_data);
224
225        BinDecoder::new(
226            self.raw_message_for_rdata_parsing
227                .as_ref()
228                .expect("None raw_message_for_rdata_parsing"),
229        )
230        .clone(index as u16)
231    }
232
233    fn parse_wks_rdata(
234        &mut self,
235        raw_rdata: &[u8],
236    ) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
237        let mut decoder = BinDecoder::new(raw_rdata);
238        let address = parse_ipv4_address(&mut decoder)?;
239        let protocol = parse_u8(&mut decoder)?;
240        let port = {
241            let mut port_string = String::new();
242            let mut current_bit: u32 = 0;
243            while !decoder.is_empty() {
244                let mut current_byte = parse_u8(&mut decoder)?;
245                if current_byte == 0 {
246                    current_bit += 8;
247                    continue;
248                }
249                for _i in 0..8 {
250                    if current_byte & 0b1000_0000 == 0b1000_0000 {
251                        write!(port_string, "{current_bit} ").expect("can always write to String");
252                    }
253                    current_byte <<= 1;
254                    current_bit += 1;
255                }
256            }
257            port_string
258        };
259        Ok((
260            Some(format!("{} {} {}", address, protocol, port.trim_end())),
261            None,
262        ))
263    }
264
265    fn parse_a6_rdata(
266        &mut self,
267        raw_rdata: &[u8],
268    ) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
269        let mut decoder = BinDecoder::new(raw_rdata);
270        let prefix = parse_u8(&mut decoder)?;
271        let ipv6_address = {
272            if prefix > 128 {
273                return Err(DnsMessageParserError::SimpleError {
274                    cause: String::from("IPV6 prefix can't be greater than 128."),
275                });
276            }
277            let address_length = (128 - prefix) / 8;
278            let mut address_vec = parse_vec(&mut decoder, address_length)?;
279            if address_vec.len() < 16 {
280                let pad_len = 16 - address_length;
281                let mut padded_address_vec = vec![0; pad_len as usize];
282                padded_address_vec.extend(&address_vec);
283                address_vec = padded_address_vec;
284            }
285            let mut dec = BinDecoder::new(&address_vec);
286            parse_ipv6_address(&mut dec)?
287        };
288        let domain_name = Self::parse_domain_name(&mut decoder, &self.options)?;
289        Ok((Some(format!("{prefix} {ipv6_address} {domain_name}")), None))
290    }
291
292    fn parse_loc_rdata(
293        &mut self,
294        raw_rdata: &[u8],
295    ) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
296        let mut decoder = BinDecoder::new(raw_rdata);
297        let _max_latitude: u32 = 0x8000_0000 + 90 * 3_600_000;
298        let _min_latitude: u32 = 0x8000_0000 - 90 * 3_600_000;
299        let _max_longitude: u32 = 0x8000_0000 + 180 * 3_600_000;
300        let _min_longitude: u32 = 0x8000_0000 - 180 * 3_600_000;
301        let _version = parse_u8(&mut decoder)?;
302        if _version != 0 {
303            return Err(DnsMessageParserError::SimpleError {
304                cause: String::from("LOC record version should be 0."),
305            });
306        }
307        let size = parse_loc_rdata_size(parse_u8(&mut decoder)?)?;
308        let horizontal_precision = parse_loc_rdata_size(parse_u8(&mut decoder)?)?;
309        let vertical_precision = parse_loc_rdata_size(parse_u8(&mut decoder)?)?;
310
311        let latitude = {
312            let received_lat = parse_u32(&mut decoder)?;
313            if received_lat < _min_latitude || received_lat > _max_latitude {
314                return Err(DnsMessageParserError::SimpleError {
315                    cause: String::from("LOC record latitude out of bounds"),
316                });
317            }
318            let dir = if received_lat > 0x8000_0000 { "N" } else { "S" };
319            parse_loc_rdata_coordinates(received_lat, dir)
320        };
321
322        let longitude = {
323            let received_lon = parse_u32(&mut decoder)?;
324            if received_lon < _min_longitude || received_lon > _max_longitude {
325                return Err(DnsMessageParserError::SimpleError {
326                    cause: String::from("LOC record longitude out of bounds"),
327                });
328            }
329            let dir = if received_lon > 0x8000_0000 { "E" } else { "W" };
330            parse_loc_rdata_coordinates(received_lon, dir)
331        };
332        let altitude = (parse_u32(&mut decoder)? as f64 - 10_000_000.0) / 100.0;
333
334        Ok((
335            Some(format!(
336                "{latitude} {longitude} {altitude:.2}m {size}m {horizontal_precision}m {vertical_precision}m"
337            )),
338            None,
339        ))
340    }
341
342    fn parse_apl_rdata(
343        &mut self,
344        raw_rdata: &[u8],
345    ) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
346        let mut decoder = BinDecoder::new(raw_rdata);
347        let mut apl_rdata = "".to_string();
348        while !decoder.is_empty() {
349            let address_family = parse_u16(&mut decoder)?;
350            let prefix = parse_u8(&mut decoder)?;
351            let mut afd_length = parse_u8(&mut decoder)?;
352            let negation = if afd_length > 127 {
353                afd_length -= 128;
354                "!"
355            } else {
356                ""
357            };
358            let mut address_vec = parse_vec(&mut decoder, afd_length)?;
359            let address = if address_family == 1 {
360                if afd_length < 4 {
361                    address_vec.resize(4, 0);
362                }
363                let mut dec = BinDecoder::new(&address_vec);
364                parse_ipv4_address(&mut dec)?
365            } else {
366                if afd_length < 16 {
367                    address_vec.resize(16, 0);
368                }
369                let mut dec = BinDecoder::new(&address_vec);
370                parse_ipv6_address(&mut dec)?
371            };
372            write!(apl_rdata, "{negation}{address_family}:{address}/{prefix}")
373                .expect("can always write to String");
374            apl_rdata.push(' ');
375        }
376        Ok((Some(apl_rdata.trim_end().to_string()), None))
377    }
378
379    pub fn format_unknown_rdata(
380        &mut self,
381        code: u16,
382        rdata: &NULL,
383    ) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
384        match code {
385            dns_message::RTYPE_MB => {
386                let options = self.options.clone();
387                let mut decoder = self.get_rdata_decoder_with_raw_message(&rdata.anything);
388                let madname = Self::parse_domain_name(&mut decoder, &options)?;
389                Ok((Some(madname), None))
390            }
391
392            dns_message::RTYPE_MG => {
393                let options = self.options.clone();
394                let mut decoder = self.get_rdata_decoder_with_raw_message(&rdata.anything);
395                let mgname = Self::parse_domain_name(&mut decoder, &options)?;
396                Ok((Some(mgname), None))
397            }
398
399            dns_message::RTYPE_MR => {
400                let options = self.options.clone();
401                let mut decoder = self.get_rdata_decoder_with_raw_message(&rdata.anything);
402                let newname = Self::parse_domain_name(&mut decoder, &options)?;
403                Ok((Some(newname), None))
404            }
405
406            dns_message::RTYPE_WKS => self.parse_wks_rdata(&rdata.anything),
407
408            dns_message::RTYPE_MINFO => {
409                let options = self.options.clone();
410                let mut decoder = self.get_rdata_decoder_with_raw_message(&rdata.anything);
411                let rmailbx = Self::parse_domain_name(&mut decoder, &options)?;
412                let emailbx = Self::parse_domain_name(&mut decoder, &options)?;
413                Ok((Some(format!("{rmailbx} {emailbx}")), None))
414            }
415
416            dns_message::RTYPE_RP => {
417                let options = self.options.clone();
418                let mut decoder = self.get_rdata_decoder_with_raw_message(&rdata.anything);
419                let mbox = Self::parse_domain_name(&mut decoder, &options)?;
420                let txt = Self::parse_domain_name(&mut decoder, &options)?;
421                Ok((Some(format!("{mbox} {txt}")), None))
422            }
423
424            dns_message::RTYPE_AFSDB => {
425                let options = self.options.clone();
426                let mut decoder = self.get_rdata_decoder_with_raw_message(&rdata.anything);
427                let subtype = parse_u16(&mut decoder)?;
428                let hostname = Self::parse_domain_name(&mut decoder, &options)?;
429                Ok((Some(format!("{subtype} {hostname}")), None))
430            }
431
432            dns_message::RTYPE_X25 => {
433                let mut decoder = BinDecoder::new(&rdata.anything);
434                let psdn_address = parse_character_string(&mut decoder)?;
435                Ok((
436                    Some(format!(
437                        "\"{}\"",
438                        escape_string_for_text_representation(psdn_address)
439                    )),
440                    None,
441                ))
442            }
443
444            dns_message::RTYPE_ISDN => {
445                let mut decoder = BinDecoder::new(&rdata.anything);
446                let address = parse_character_string(&mut decoder)?;
447                if decoder.is_empty() {
448                    Ok((
449                        Some(format!(
450                            "\"{}\"",
451                            escape_string_for_text_representation(address)
452                        )),
453                        None,
454                    ))
455                } else {
456                    let sub_address = parse_character_string(&mut decoder)?;
457                    Ok((
458                        Some(format!(
459                            "\"{}\" \"{}\"",
460                            escape_string_for_text_representation(address),
461                            escape_string_for_text_representation(sub_address)
462                        )),
463                        None,
464                    ))
465                }
466            }
467
468            dns_message::RTYPE_RT => {
469                let options = self.options.clone();
470                let mut decoder = self.get_rdata_decoder_with_raw_message(&rdata.anything);
471                let preference = parse_u16(&mut decoder)?;
472                let intermediate_host = Self::parse_domain_name(&mut decoder, &options)?;
473                Ok((Some(format!("{preference} {intermediate_host}")), None))
474            }
475
476            dns_message::RTYPE_NSAP => {
477                let raw_rdata = &rdata.anything;
478                let mut decoder = BinDecoder::new(raw_rdata);
479                let rdata_len = raw_rdata.len() as u16;
480                let nsap_rdata = HEXUPPER.encode(&parse_vec_with_u16_len(&mut decoder, rdata_len)?);
481                Ok((Some(format!("0x{nsap_rdata}")), None))
482            }
483
484            dns_message::RTYPE_PX => {
485                let options = self.options.clone();
486                let mut decoder = self.get_rdata_decoder_with_raw_message(&rdata.anything);
487                let preference = parse_u16(&mut decoder)?;
488                let map822 = Self::parse_domain_name(&mut decoder, &options)?;
489                let mapx400 = Self::parse_domain_name(&mut decoder, &options)?;
490                Ok((Some(format!("{preference} {map822} {mapx400}")), None))
491            }
492
493            dns_message::RTYPE_LOC => self.parse_loc_rdata(&rdata.anything),
494
495            dns_message::RTYPE_KX => {
496                let options = self.options.clone();
497                let mut decoder = self.get_rdata_decoder_with_raw_message(&rdata.anything);
498                let preference = parse_u16(&mut decoder)?;
499                let exchanger = Self::parse_domain_name(&mut decoder, &options)?;
500                Ok((Some(format!("{preference} {exchanger}")), None))
501            }
502
503            dns_message::RTYPE_A6 => self.parse_a6_rdata(&rdata.anything),
504
505            dns_message::RTYPE_SINK => {
506                let raw_rdata = &rdata.anything;
507                let mut decoder = BinDecoder::new(raw_rdata);
508                let meaning = parse_u8(&mut decoder)?;
509                let coding = parse_u8(&mut decoder)?;
510                let subcoding = parse_u8(&mut decoder)?;
511                let data_len = raw_rdata.len() as u16 - 3;
512                let data = BASE64.encode(&parse_vec_with_u16_len(&mut decoder, data_len)?);
513
514                Ok((Some(format!("{meaning} {coding} {subcoding} {data}")), None))
515            }
516
517            dns_message::RTYPE_APL => self.parse_apl_rdata(&rdata.anything),
518
519            dns_message::RTYPE_DHCID => {
520                let raw_rdata = &rdata.anything;
521                let mut decoder = BinDecoder::new(raw_rdata);
522                let raw_data_len = raw_rdata.len() as u16;
523                let digest = BASE64.encode(&parse_vec_with_u16_len(&mut decoder, raw_data_len)?);
524                Ok((Some(digest), None))
525            }
526
527            dns_message::RTYPE_SPF => {
528                let mut decoder = BinDecoder::new(&rdata.anything);
529                let mut text = String::new();
530                while !decoder.is_empty() {
531                    text.push('\"');
532                    text.push_str(&parse_character_string(&mut decoder)?);
533                    text.push_str("\" ");
534                }
535                Ok((Some(text.trim_end().to_string()), None))
536            }
537
538            _ => Ok((None, Some(rdata.anything.clone()))),
539        }
540    }
541
542    fn format_rdata(&self, rdata: &RData) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
543        match rdata {
544            RData::A(ip) => Ok((Some(ip.to_string()), None)),
545            RData::AAAA(ip) => Ok((Some(ip.to_string()), None)),
546            RData::ANAME(name) => Ok((Some(name.to_string_with_options(&self.options)), None)),
547            RData::CNAME(name) => Ok((Some(name.to_string_with_options(&self.options)), None)),
548            RData::CERT(cert) => {
549                let crl = BASE64.encode(&cert.cert_data);
550                Ok((
551                    Some(format!(
552                        "{} {} {} {}",
553                        u16::from(cert.cert_type),
554                        cert.key_tag,
555                        cert.algorithm,
556                        crl
557                    )),
558                    None,
559                ))
560            }
561
562            RData::CSYNC(csync) => {
563                // Using CSYNC's formatter since not all data is exposed otherwise
564                let csync_rdata = format!("{csync}");
565                Ok((Some(csync_rdata), None))
566            }
567            RData::MX(mx) => {
568                let srv_rdata = format!(
569                    "{} {}",
570                    mx.preference,
571                    mx.exchange.to_string_with_options(&self.options),
572                );
573                Ok((Some(srv_rdata), None))
574            }
575            RData::NULL(null) => Ok((Some(BASE64.encode(&null.anything)), None)),
576            RData::NS(ns) => Ok((Some(ns.to_string_with_options(&self.options)), None)),
577            RData::OPENPGPKEY(key) => {
578                if let Ok(key_string) = String::from_utf8(key.public_key.clone()) {
579                    Ok((Some(format!("({})", &key_string)), None))
580                } else {
581                    Err(DnsMessageParserError::SimpleError {
582                        cause: String::from("Invalid OPENPGPKEY rdata"),
583                    })
584                }
585            }
586            RData::PTR(ptr) => Ok((Some(ptr.to_string_with_options(&self.options)), None)),
587            RData::SOA(soa) => Ok((
588                Some(format!(
589                    "{} {} {} {} {} {} {}",
590                    soa.mname.to_string_with_options(&self.options),
591                    soa.rname.to_string_with_options(&self.options),
592                    soa.serial,
593                    soa.refresh,
594                    soa.retry,
595                    soa.expire,
596                    soa.minimum
597                )),
598                None,
599            )),
600            RData::SRV(srv) => {
601                let srv_rdata = format!(
602                    "{} {} {} {}",
603                    srv.priority,
604                    srv.weight,
605                    srv.port,
606                    srv.target.to_string_with_options(&self.options)
607                );
608                Ok((Some(srv_rdata), None))
609            }
610            RData::TXT(txt) => {
611                let txt_rdata = txt
612                    .txt_data
613                    .iter()
614                    .map(|value| {
615                        format!(
616                            "\"{}\"",
617                            escape_string_for_text_representation(
618                                String::from_utf8_lossy(value).to_string()
619                            )
620                        )
621                    })
622                    .collect::<Vec<String>>()
623                    .join(" ");
624                Ok((Some(txt_rdata), None))
625            }
626            RData::CAA(caa) => {
627                let caa_rdata = format!(
628                    "{} {} \"{}\"",
629                    caa.issuer_critical as u8,
630                    &caa.tag,
631                    if caa.tag.eq_ignore_ascii_case("iodef") {
632                        let url = caa
633                            .value_as_iodef()
634                            .map_err(|source| DnsMessageParserError::TrustDnsError { source })?;
635                        url.as_str().to_string()
636                    } else if caa.tag.eq_ignore_ascii_case("issue")
637                        || caa.tag.eq_ignore_ascii_case("issuewild")
638                    {
639                        let (option_name, vec_keyvalue) = caa
640                            .value_as_issue()
641                            .map_err(|source| DnsMessageParserError::TrustDnsError { source })?;
642
643                        let mut final_issuer = String::new();
644                        if let Some(name) = option_name {
645                            final_issuer.push_str(&name.to_string_with_options(&self.options));
646                            for keyvalue in vec_keyvalue.iter() {
647                                final_issuer.push_str("; ");
648                                final_issuer.push_str(keyvalue.key());
649                                final_issuer.push('=');
650                                final_issuer.push_str(keyvalue.value());
651                            }
652                        }
653                        final_issuer.trim_end().to_string()
654                    } else {
655                        std::str::from_utf8(&caa.value)
656                            .map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?
657                            .to_string()
658                    }
659                );
660                Ok((Some(caa_rdata), None))
661            }
662
663            RData::TLSA(tlsa) => {
664                let tlsa_rdata = format!(
665                    "{} {} {} {}",
666                    u8::from(tlsa.cert_usage),
667                    u8::from(tlsa.selector),
668                    u8::from(tlsa.matching),
669                    HEXUPPER.encode(&tlsa.cert_data)
670                );
671                Ok((Some(tlsa_rdata), None))
672            }
673            RData::SSHFP(sshfp) => {
674                let sshfp_rdata = format!(
675                    "{} {} {}",
676                    Into::<u8>::into(sshfp.algorithm),
677                    Into::<u8>::into(sshfp.fingerprint_type),
678                    HEXUPPER.encode(&sshfp.fingerprint)
679                );
680                Ok((Some(sshfp_rdata), None))
681            }
682            RData::NAPTR(naptr) => {
683                let naptr_rdata = format!(
684                    r#"{} {} "{}" "{}" "{}" {}"#,
685                    naptr.order,
686                    naptr.preference,
687                    escape_string_for_text_representation(
688                        std::str::from_utf8(&naptr.flags)
689                            .map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?
690                            .to_string()
691                    ),
692                    escape_string_for_text_representation(
693                        std::str::from_utf8(&naptr.services)
694                            .map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?
695                            .to_string()
696                    ),
697                    escape_string_for_text_representation(
698                        std::str::from_utf8(&naptr.regexp)
699                            .map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?
700                            .to_string()
701                    ),
702                    naptr.replacement.to_string_with_options(&self.options)
703                );
704                Ok((Some(naptr_rdata), None))
705            }
706            RData::HINFO(hinfo) => {
707                let hinfo_data = format!(
708                    r#""{}" "{}""#,
709                    std::str::from_utf8(&hinfo.cpu)
710                        .map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?,
711                    std::str::from_utf8(&hinfo.os)
712                        .map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?,
713                );
714                Ok((Some(hinfo_data), None))
715            }
716            RData::HTTPS(https) => {
717                let https_data = format_svcb_record(&https.0, &self.options);
718                Ok((Some(https_data), None))
719            }
720            RData::SVCB(svcb) => {
721                let svcb_data = format_svcb_record(svcb, &self.options);
722                Ok((Some(svcb_data), None))
723            }
724            RData::OPT(opt) => {
725                let parsed = parse_edns_options(opt)?;
726                let ede_data = parsed.0.iter().map(|entry| EdnsOptionEntry {
727                    opt_code: 15u16,
728                    opt_name: "EDE".to_string(),
729                    opt_data: format!(
730                        "EDE={}({}){}",
731                        entry.info_code(),
732                        entry.purpose().unwrap_or(""),
733                        entry.extra_text().unwrap_or("".to_string())
734                    ),
735                });
736                let opt_data = parsed
737                    .1
738                    .into_iter()
739                    .chain(ede_data)
740                    .map(|entry| format!("{}={}", entry.opt_name, entry.opt_data))
741                    .collect::<Vec<String>>()
742                    .join(",");
743                Ok((Some(opt_data), None))
744            }
745            RData::DNSSEC(dnssec) => match dnssec {
746                // See https://tools.ietf.org/html/rfc4034 for details
747                // on dnssec related rdata formats
748                DNSSECRData::CDS(cds) => Ok((Some(format_cds_record(cds)), None)),
749                DNSSECRData::DS(ds) => Ok((Some(format_ds_record(ds)), None)),
750                DNSSECRData::CDNSKEY(cdnskey) => Ok((Some(format_cdnskey_record(cdnskey)), None)),
751                DNSSECRData::DNSKEY(dnskey) => Ok((Some(format_dnskey_record(dnskey)), None)),
752                DNSSECRData::NSEC(nsec) => {
753                    let nsec_rdata = format!(
754                        "{} {}",
755                        nsec.next_domain_name()
756                            .to_string_with_options(&self.options),
757                        nsec.type_bit_maps()
758                            .flat_map(format_record_type)
759                            .collect::<Vec<String>>()
760                            .join(" ")
761                    );
762                    Ok((Some(nsec_rdata), None))
763                }
764                DNSSECRData::NSEC3(nsec3) => {
765                    let nsec3_rdata = format!(
766                        "{} {} {} {} {} {}",
767                        u8::from(nsec3.hash_algorithm()),
768                        nsec3.opt_out() as u8,
769                        nsec3.iterations(),
770                        HEXUPPER.encode(nsec3.salt()),
771                        BASE32HEX_NOPAD.encode(nsec3.next_hashed_owner_name()),
772                        nsec3
773                            .type_bit_maps()
774                            .flat_map(format_record_type)
775                            .collect::<Vec<String>>()
776                            .join(" ")
777                    );
778                    Ok((Some(nsec3_rdata), None))
779                }
780                DNSSECRData::NSEC3PARAM(nsec3param) => {
781                    let nsec3param_rdata = format!(
782                        "{} {} {} {}",
783                        u8::from(nsec3param.hash_algorithm()),
784                        nsec3param.opt_out() as u8,
785                        nsec3param.iterations(),
786                        HEXUPPER.encode(nsec3param.salt()),
787                    );
788                    Ok((Some(nsec3param_rdata), None))
789                }
790
791                DNSSECRData::SIG(sig) => {
792                    let sig_rdata = format!(
793                        "{} {} {} {} {} {} {} {} {}",
794                        match format_record_type(sig.input().type_covered) {
795                            Some(record_type) => record_type,
796                            None => String::from("Unknown record type"),
797                        },
798                        u8::from(sig.input().algorithm),
799                        sig.input().num_labels,
800                        sig.input().original_ttl,
801                        sig.input().sig_expiration.get(), // currently in epoch convert to human readable ?
802                        sig.input().sig_inception.get(), // currently in epoch convert to human readable ?
803                        sig.input().key_tag,
804                        sig.input()
805                            .signer_name
806                            .to_string_with_options(&self.options),
807                        BASE64.encode(sig.sig())
808                    );
809                    Ok((Some(sig_rdata), None))
810                }
811                // RRSIG is a derivation of SIG but choosing to keep this duplicate code in lieu of the alternative
812                // which is to allocate to the heap with Box in order to deref.
813                DNSSECRData::RRSIG(sig) => {
814                    let sig_rdata = format!(
815                        "{} {} {} {} {} {} {} {} {}",
816                        match format_record_type(sig.input().type_covered) {
817                            Some(record_type) => record_type,
818                            None => String::from("Unknown record type"),
819                        },
820                        u8::from(sig.input().algorithm),
821                        sig.input().num_labels,
822                        sig.input().original_ttl,
823                        sig.input().sig_expiration.get(), // currently in epoch convert to human readable ?
824                        sig.input().sig_inception.get(), // currently in epoch convert to human readable ?
825                        sig.input().key_tag,
826                        sig.input()
827                            .signer_name
828                            .to_string_with_options(&self.options),
829                        BASE64.encode(sig.sig())
830                    );
831                    Ok((Some(sig_rdata), None))
832                }
833                DNSSECRData::KEY(key) => {
834                    let key_rdata = format!(
835                        "{} {} {} {}",
836                        key.flags(),
837                        u8::from(key.protocol()),
838                        u8::from(key.algorithm()),
839                        BASE64.encode(key.public_key())
840                    );
841                    Ok((Some(key_rdata), None))
842                }
843                DNSSECRData::Unknown { code: _, rdata } => Ok((None, Some(rdata.anything.clone()))),
844                _ => Err(DnsMessageParserError::SimpleError {
845                    cause: format!("Unsupported rdata {rdata:?}"),
846                }),
847            },
848            _ => Err(DnsMessageParserError::SimpleError {
849                cause: format!("Unsupported rdata {rdata:?}"),
850            }),
851        }
852    }
853
854    fn parse_domain_name(
855        decoder: &mut BinDecoder<'_>,
856        options: &DnsParserOptions,
857    ) -> DnsParserResult<String> {
858        parse_domain_name(decoder).map(|n| n.to_string_with_options(options))
859    }
860}
861
862fn format_record_type(record_type: RecordType) -> Option<String> {
863    match record_type {
864        RecordType::Unknown(code) => parse_unknown_record_type(code),
865        _ => Some(record_type.to_string()),
866    }
867}
868
869fn format_svcb_record(svcb: &SVCB, options: &DnsParserOptions) -> String {
870    format!(
871        "{} {} {}",
872        svcb.svc_priority,
873        svcb.target_name.to_string_with_options(options),
874        svcb.svc_params
875            .iter()
876            .map(|(key, value)| format!(r#"{}="{}""#, key, value.to_string().trim_end_matches(',')))
877            .collect::<Vec<_>>()
878            .join(" ")
879    )
880}
881
882fn format_cdnskey_record(cdnskey: &CDNSKEY) -> String {
883    format!(
884        "{} 3 {} {}",
885        {
886            if cdnskey.revoke() {
887                0b0000_0000_0000_0000
888            } else if cdnskey.zone_key() && cdnskey.secure_entry_point() {
889                0b0000_0001_0000_0001
890            } else {
891                0b0000_0001_0000_0000
892            }
893        },
894        cdnskey.algorithm().map_or(0, u8::from),
895        cdnskey
896            .public_key()
897            .map_or("".to_string(), |k| BASE64.encode(k.public_bytes()))
898    )
899}
900
901fn format_dnskey_record(dnskey: &DNSKEY) -> String {
902    format!(
903        "{} 3 {} {}",
904        {
905            if dnskey.revoke() {
906                0b0000_0000_0000_0000
907            } else if dnskey.zone_key() && dnskey.secure_entry_point() {
908                0b0000_0001_0000_0001
909            } else {
910                0b0000_0001_0000_0000
911            }
912        },
913        u8::from(dnskey.algorithm()),
914        BASE64.encode(dnskey.public_key().public_bytes())
915    )
916}
917
918fn format_cds_record(cds: &CDS) -> String {
919    format!(
920        "{} {} {} {}",
921        cds.key_tag(),
922        cds.algorithm().map_or(0, u8::from),
923        u8::from(cds.digest_type()),
924        HEXUPPER.encode(cds.digest())
925    )
926}
927
928fn format_ds_record(ds: &DS) -> String {
929    format!(
930        "{} {} {} {}",
931        ds.key_tag(),
932        u8::from(ds.algorithm()),
933        u8::from(ds.digest_type()),
934        HEXUPPER.encode(ds.digest())
935    )
936}
937
938fn parse_response_code(rcode: u16) -> Option<&'static str> {
939    match rcode {
940        0 => Some("NoError"), // 0    NoError    No Error                             [RFC1035]
941        1 => Some("FormErr"), // 1    FormErr    Format Error                         [RFC1035]
942        2 => Some("ServFail"), // 2    ServFail   Server Failure                       [RFC1035]
943        3 => Some("NXDomain"), // 3    NXDomain   Non-Existent Domain                  [RFC1035]
944        4 => Some("NotImp"),  // 4    NotImp     Not Implemented                      [RFC1035]
945        5 => Some("Refused"), // 5    Refused    Query Refused                        [RFC1035]
946        6 => Some("YXDomain"), // 6    YXDomain   Name Exists when it should not       [RFC2136][RFC6672]
947        7 => Some("YXRRSet"),  // 7    YXRRSet    RR Set Exists when it should not     [RFC2136]
948        8 => Some("NXRRSet"),  // 8    NXRRSet    RR Set that should exist does not    [RFC2136]
949        9 => Some("NotAuth"),  // 9    NotAuth    Server Not Authoritative for zone    [RFC2136]
950        10 => Some("NotZone"), // 10   NotZone    Name not contained in zone           [RFC2136]
951        // backwards compat for 4 bit ResponseCodes so far.
952        16 => Some("BADVERS"), // 16    BADVERS    Bad OPT Version    [RFC6891]
953        // 16    BADSIG    TSIG Signature Failure               [RFC2845]
954        17 => Some("BADKEY"), // 17    BADKEY    Key not recognized                   [RFC2845]
955        18 => Some("BADTIME"), // 18    BADTIME   Signature out of time window         [RFC2845]
956        19 => Some("BADMODE"), // 19    BADMODE   Bad TKEY Mode                        [RFC2930]
957        20 => Some("BADNAME"), // 20    BADNAME   Duplicate key name                   [RFC2930]
958        21 => Some("BADALG"), // 21    BADALG    Algorithm not supported              [RFC2930]
959        22 => Some("BADTRUNC"), // 22    BADTRUNC  Bad Truncation                       [RFC4635]
960        23 => Some("BADCOOKIE"), // 23    BADCOOKIE (TEMPORARY - registered 2015-07-26, expires 2016-07-26)    Bad/missing server cookie    [draft-ietf-dnsop-cookies]
961        _ => None,
962    }
963}
964
965fn parse_dns_query_message_header(dns_message: &TrustDnsMessage) -> QueryHeader {
966    QueryHeader {
967        id: dns_message.id,
968        opcode: dns_message.op_code.into(),
969        rcode: dns_message.response_code,
970        qr: dns_message.message_type as u8,
971        aa: dns_message.authoritative,
972        tc: dns_message.truncation,
973        rd: dns_message.recursion_desired,
974        ra: dns_message.recursion_available,
975        ad: dns_message.authentic_data,
976        cd: dns_message.checking_disabled,
977        question_count: dns_message.queries.len() as u16,
978        answer_count: dns_message.answers.len() as u16,
979        authority_count: dns_message.authorities.len() as u16,
980        additional_count: dns_message.additionals.len() as u16 + dns_message.edns.is_some() as u16,
981    }
982}
983
984fn parse_dns_update_message_header(dns_message: &TrustDnsMessage) -> UpdateHeader {
985    UpdateHeader {
986        id: dns_message.id,
987        opcode: dns_message.op_code.into(),
988        rcode: dns_message.response_code,
989        qr: dns_message.message_type as u8,
990        zone_count: dns_message.queries.len() as u16,
991        prerequisite_count: dns_message.answers.len() as u16,
992        update_count: dns_message.authorities.len() as u16,
993        additional_count: dns_message.additionals.len() as u16 + dns_message.edns.is_some() as u16,
994    }
995}
996
997fn parse_edns(dns_message: &TrustDnsMessage) -> Option<DnsParserResult<OptPseudoSection>> {
998    dns_message.edns.as_ref().map(|edns| {
999        parse_edns_options(edns.options()).map(|(ede, rest)| OptPseudoSection {
1000            extended_rcode: edns.rcode_high(),
1001            version: edns.version(),
1002            dnssec_ok: edns.flags().dnssec_ok,
1003            udp_max_payload_size: edns.max_payload(),
1004            ede,
1005            options: rest,
1006        })
1007    })
1008}
1009
1010fn parse_edns_options(edns: &OPT) -> DnsParserResult<(Vec<EDE>, Vec<EdnsOptionEntry>)> {
1011    let ede_opts: Vec<EDE> = edns
1012        .as_ref()
1013        .iter()
1014        .filter_map(|(_, option)| {
1015            if let EdnsOption::Unknown(EDE_OPTION_CODE, option) = option {
1016                Some(EDE::from_bytes(option).map_err(|source| {
1017                    DnsMessageParserError::TrustDnsError {
1018                        source: ProtoError::from(source),
1019                    }
1020                }))
1021            } else {
1022                None
1023            }
1024        })
1025        .collect::<Result<Vec<EDE>, DnsMessageParserError>>()?;
1026
1027    let rest: Vec<EdnsOptionEntry> = edns
1028        .as_ref()
1029        .iter()
1030        .filter(|(code, _)| u16::from(*code) != EDE_OPTION_CODE)
1031        .map(|(code, option)| match option {
1032            EdnsOption::DAU(algorithms) => Ok(parse_edns_opt_dnssec_algorithms(*code, *algorithms)),
1033            EdnsOption::Unknown(_, opt_data) => Ok(parse_edns_opt(*code, opt_data)),
1034            option => Vec::<u8>::try_from(option)
1035                .map(|bytes| parse_edns_opt(*code, &bytes))
1036                .map_err(|source| DnsMessageParserError::TrustDnsError { source }),
1037        })
1038        .collect::<Result<Vec<EdnsOptionEntry>, DnsMessageParserError>>()?;
1039
1040    Ok((ede_opts, rest))
1041}
1042
1043fn parse_edns_opt_dnssec_algorithms(
1044    opt_code: EdnsCode,
1045    algorithms: SupportedAlgorithms,
1046) -> EdnsOptionEntry {
1047    let algorithm_names: Vec<String> = algorithms.iter().map(|alg| alg.to_string()).collect();
1048    EdnsOptionEntry {
1049        opt_code: Into::<u16>::into(opt_code),
1050        opt_name: format!("{opt_code:?}"),
1051        opt_data: algorithm_names.join(" "),
1052    }
1053}
1054
1055fn parse_edns_opt(opt_code: EdnsCode, opt_data: &[u8]) -> EdnsOptionEntry {
1056    EdnsOptionEntry {
1057        opt_code: Into::<u16>::into(opt_code),
1058        opt_name: format!("{opt_code:?}"),
1059        opt_data: BASE64.encode(opt_data),
1060    }
1061}
1062
1063fn parse_loc_rdata_size(data: u8) -> DnsParserResult<f64> {
1064    let base = (data & 0xF0) >> 4;
1065    if base > 9 {
1066        return Err(DnsMessageParserError::SimpleError {
1067            cause: format!("The base shouldn't be greater than 9. Base: {base}"),
1068        });
1069    }
1070
1071    let exponent = data & 0x0F;
1072    if exponent > 9 {
1073        return Err(DnsMessageParserError::SimpleError {
1074            cause: format!("The exponent shouldn't be greater than 9. Exponent: {exponent}"),
1075        });
1076    }
1077
1078    let ten: u64 = 10;
1079    let ans = (base as f64) * ten.pow(exponent as u32) as f64;
1080    Ok(ans / 100.0) // convert cm to metre
1081}
1082
1083fn parse_loc_rdata_coordinates(coordinates: u32, dir: &str) -> String {
1084    let degree = (coordinates as i64 - 0x8000_0000) as f64 / 3_600_000.00;
1085    let minute = degree.fract() * 60.0;
1086    let second = minute.fract() * 60.0;
1087
1088    format!(
1089        "{} {} {:.3} {}",
1090        degree.trunc().abs(),
1091        minute.trunc().abs(),
1092        second.abs(),
1093        dir
1094    )
1095}
1096
1097fn parse_character_string(decoder: &mut BinDecoder<'_>) -> DnsParserResult<String> {
1098    let raw_len = decoder
1099        .read_u8()
1100        .map_err(|source| DnsMessageParserError::TrustDnsError {
1101            source: ProtoError::from(source),
1102        })?;
1103    let len = raw_len.unverified() as usize;
1104    let raw_text =
1105        decoder
1106            .read_slice(len)
1107            .map_err(|source| DnsMessageParserError::TrustDnsError {
1108                source: ProtoError::from(source),
1109            })?;
1110    match raw_text.verify_unwrap(|r| r.len() == len) {
1111        Ok(verified_text) => Ok(String::from_utf8_lossy(verified_text).to_string()),
1112        Err(raw_data) => Err(DnsMessageParserError::SimpleError {
1113            cause: format!(
1114                "Unexpected data length: expected {}, got {}. Raw data {}",
1115                len,
1116                raw_data.len(),
1117                format_bytes_as_hex_string(raw_data)
1118            ),
1119        }),
1120    }
1121}
1122
1123fn parse_u8(decoder: &mut BinDecoder<'_>) -> DnsParserResult<u8> {
1124    Ok(decoder
1125        .read_u8()
1126        .map_err(|source| DnsMessageParserError::TrustDnsError {
1127            source: ProtoError::from(source),
1128        })?
1129        .unverified())
1130}
1131
1132fn parse_u16(decoder: &mut BinDecoder<'_>) -> DnsParserResult<u16> {
1133    Ok(decoder
1134        .read_u16()
1135        .map_err(|source| DnsMessageParserError::TrustDnsError {
1136            source: ProtoError::from(source),
1137        })?
1138        .unverified())
1139}
1140
1141fn parse_u32(decoder: &mut BinDecoder<'_>) -> DnsParserResult<u32> {
1142    Ok(decoder
1143        .read_u32()
1144        .map_err(|source| DnsMessageParserError::TrustDnsError {
1145            source: ProtoError::from(source),
1146        })?
1147        .unverified())
1148}
1149
1150fn parse_vec(decoder: &mut BinDecoder<'_>, buffer_len: u8) -> DnsParserResult<Vec<u8>> {
1151    let len = buffer_len as usize;
1152    Ok(decoder
1153        .read_vec(len)
1154        .map_err(|source| DnsMessageParserError::TrustDnsError {
1155            source: ProtoError::from(source),
1156        })?
1157        .unverified())
1158}
1159
1160fn parse_vec_with_u16_len(
1161    decoder: &mut BinDecoder<'_>,
1162    buffer_len: u16,
1163) -> DnsParserResult<Vec<u8>> {
1164    let len = buffer_len as usize;
1165    Ok(decoder
1166        .read_vec(len)
1167        .map_err(|source| DnsMessageParserError::TrustDnsError {
1168            source: ProtoError::from(source),
1169        })?
1170        .unverified())
1171}
1172
1173fn parse_ipv6_address(decoder: &mut BinDecoder<'_>) -> DnsParserResult<String> {
1174    Ok(<AAAA as BinDecodable>::read(decoder)
1175        .map_err(|source| DnsMessageParserError::TrustDnsError {
1176            source: ProtoError::from(source),
1177        })?
1178        .to_string())
1179}
1180
1181fn parse_ipv4_address(decoder: &mut BinDecoder<'_>) -> DnsParserResult<String> {
1182    Ok(<A as BinDecodable>::read(decoder)
1183        .map_err(|source| DnsMessageParserError::TrustDnsError {
1184            source: ProtoError::from(source),
1185        })?
1186        .to_string())
1187}
1188
1189fn parse_domain_name(decoder: &mut BinDecoder<'_>) -> DnsParserResult<Name> {
1190    Name::read(decoder).map_err(|source| DnsMessageParserError::TrustDnsError {
1191        source: ProtoError::from(source),
1192    })
1193}
1194
1195fn escape_string_for_text_representation(original_string: String) -> String {
1196    original_string.replace('\\', "\\\\").replace('\"', "\\\"")
1197}
1198
1199fn parse_unknown_record_type(rtype: u16) -> Option<String> {
1200    match rtype {
1201        1 => Some(String::from("A")),
1202        2 => Some(String::from("NS")),
1203        3 => Some(String::from("MD")),
1204        4 => Some(String::from("MF")),
1205        5 => Some(String::from("CNAME")),
1206        6 => Some(String::from("SOA")),
1207        7 => Some(String::from("MB")),
1208        8 => Some(String::from("MG")),
1209        9 => Some(String::from("MR")),
1210        10 => Some(String::from("NULL")),
1211        11 => Some(String::from("WKS")),
1212        12 => Some(String::from("PTR")),
1213        13 => Some(String::from("HINFO")),
1214        14 => Some(String::from("MINFO")),
1215        15 => Some(String::from("MX")),
1216        16 => Some(String::from("TXT")),
1217        17 => Some(String::from("RP")),
1218        18 => Some(String::from("AFSDB")),
1219        19 => Some(String::from("X25")),
1220        20 => Some(String::from("ISDN")),
1221        21 => Some(String::from("RT")),
1222        22 => Some(String::from("NSAP")),
1223        23 => Some(String::from("NSAP-PTR")),
1224        24 => Some(String::from("SIG")),
1225        25 => Some(String::from("KEY")),
1226        26 => Some(String::from("PX")),
1227        27 => Some(String::from("GPOS")),
1228        28 => Some(String::from("AAAA")),
1229        29 => Some(String::from("LOC")),
1230        30 => Some(String::from("NXT")),
1231        31 => Some(String::from("EID")),
1232        32 => Some(String::from("NIMLOC")),
1233        33 => Some(String::from("SRV")),
1234        34 => Some(String::from("ATMA")),
1235        35 => Some(String::from("NAPTR")),
1236        36 => Some(String::from("KX")),
1237        37 => Some(String::from("CERT")),
1238        38 => Some(String::from("A6")),
1239        39 => Some(String::from("DNAME")),
1240        40 => Some(String::from("SINK")),
1241        41 => Some(String::from("OPT")),
1242        42 => Some(String::from("APL")),
1243        43 => Some(String::from("DS")),
1244        44 => Some(String::from("SSHFP")),
1245        45 => Some(String::from("IPSECKEY")),
1246        46 => Some(String::from("RRSIG")),
1247        47 => Some(String::from("NSEC")),
1248        48 => Some(String::from("DNSKEY")),
1249        49 => Some(String::from("DHCID")),
1250        50 => Some(String::from("NSEC3")),
1251        51 => Some(String::from("NSEC3PARAM")),
1252        52 => Some(String::from("TLSA")),
1253        53 => Some(String::from("SMIMEA")),
1254        55 => Some(String::from("HIP")),
1255        56 => Some(String::from("NINFO")),
1256        57 => Some(String::from("RKEY")),
1257        58 => Some(String::from("TALINK")),
1258        59 => Some(String::from("CDS")),
1259        60 => Some(String::from("CDNSKEY")),
1260        61 => Some(String::from("OPENPGPKEY")),
1261        62 => Some(String::from("CSYNC")),
1262        63 => Some(String::from("ZONEMD")),
1263        99 => Some(String::from("SPF")),
1264        100 => Some(String::from("UINFO")),
1265        101 => Some(String::from("UID")),
1266        102 => Some(String::from("GID")),
1267        103 => Some(String::from("UNSPEC")),
1268        104 => Some(String::from("NID")),
1269        105 => Some(String::from("L32")),
1270        106 => Some(String::from("L64")),
1271        107 => Some(String::from("LP")),
1272        108 => Some(String::from("EUI48")),
1273        109 => Some(String::from("EUI64")),
1274        249 => Some(String::from("TKEY")),
1275        250 => Some(String::from("TSIG")),
1276        251 => Some(String::from("IXFR")),
1277        252 => Some(String::from("AXFR")),
1278        253 => Some(String::from("MAILB")),
1279        254 => Some(String::from("MAILA")),
1280        255 => Some(String::from("ANY")),
1281        256 => Some(String::from("URI")),
1282        257 => Some(String::from("CAA")),
1283        258 => Some(String::from("AVC")),
1284        259 => Some(String::from("DOA")),
1285        260 => Some(String::from("AMTRELAY")),
1286        32768 => Some(String::from("TA")),
1287        32769 => Some(String::from("DLV")),
1288
1289        _ => None,
1290    }
1291}
1292
1293fn format_bytes_as_hex_string(bytes: &[u8]) -> String {
1294    bytes
1295        .iter()
1296        .map(|e| format!("{e:02X}"))
1297        .collect::<Vec<String>>()
1298        .join(".")
1299}
1300
1301#[cfg(test)]
1302mod tests {
1303    use std::{
1304        net::{Ipv4Addr, Ipv6Addr},
1305        str::FromStr,
1306    };
1307
1308    #[allow(deprecated)]
1309    use hickory_proto::dnssec::rdata::key::UpdateScope;
1310    use hickory_proto::{
1311        dnssec::{
1312            Algorithm as DNSSEC_Algorithm, DigestType, Nsec3HashAlgorithm, PublicKeyBuf,
1313            rdata::{
1314                KEY, NSEC, NSEC3, NSEC3PARAM, RRSIG,
1315                key::{KeyTrust, KeyUsage, Protocol},
1316                sig::SigInput,
1317            },
1318        },
1319        rr::{
1320            SerialNumber,
1321            domain::Name,
1322            rdata::{
1323                CAA, CERT, CSYNC, HINFO, HTTPS, NAPTR, OPT, SSHFP, TLSA, TXT,
1324                caa::KeyValue,
1325                cert::{Algorithm as CertAlgorithm, CertType},
1326                sshfp::{Algorithm, FingerprintType},
1327                svcb,
1328                tlsa::{CertUsage, Matching, Selector},
1329            },
1330        },
1331        serialize::binary::Restrict,
1332    };
1333
1334    use super::*;
1335
1336    impl DnsMessageParser {
1337        pub fn raw_message_for_rdata_parsing(&self) -> Option<&Vec<u8>> {
1338            self.raw_message_for_rdata_parsing.as_ref()
1339        }
1340    }
1341
1342    fn format_rdata(rdata: &RData) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
1343        DnsMessageParser::new(Vec::new()).format_rdata(rdata)
1344    }
1345
1346    fn format_rdata_with_options(
1347        rdata: &RData,
1348        options: DnsParserOptions,
1349    ) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
1350        DnsMessageParser::with_options(Vec::new(), options).format_rdata(rdata)
1351    }
1352
1353    #[test]
1354    fn test_parse_as_query_message() {
1355        let raw_dns_message = "szgAAAABAAAAAAAAAmg1B2V4YW1wbGUDY29tAAAGAAE=";
1356        let raw_query_message = BASE64
1357            .decode(raw_dns_message.as_bytes())
1358            .expect("Invalid base64 encoded data.");
1359        let parse_result = DnsMessageParser::new(raw_query_message).parse_as_query_message();
1360        assert!(parse_result.is_ok());
1361        let message = parse_result.expect("Message is not parsed.");
1362        assert_eq!(message.header.qr, 0);
1363        assert_eq!(message.question_section.len(), 1);
1364        assert_eq!(
1365            message.question_section.first().unwrap().name,
1366            "h5.example.com."
1367        );
1368        assert_eq!(
1369            &message
1370                .question_section
1371                .first()
1372                .unwrap()
1373                .record_type
1374                .clone()
1375                .unwrap(),
1376            "SOA"
1377        );
1378    }
1379
1380    #[test]
1381    fn test_parse_as_query_message_with_ede() {
1382        let raw_dns_message =
1383            "szgAAAABAAAAAAABAmg1B2V4YW1wbGUDY29tAAAGAAEAACkE0AEBQAAABgAPAAIAFQ==";
1384        let raw_query_message = BASE64
1385            .decode(raw_dns_message.as_bytes())
1386            .expect("Invalid base64 encoded data.");
1387        let parse_result = DnsMessageParser::new(raw_query_message).parse_as_query_message();
1388        assert!(parse_result.is_ok());
1389        let message = parse_result.expect("Message is not parsed.");
1390        let opt_pseudo_section = message.opt_pseudo_section.expect("OPT section was missing");
1391        assert_eq!(opt_pseudo_section.ede.len(), 1);
1392        assert_eq!(opt_pseudo_section.ede[0].info_code(), 21u16);
1393        assert_eq!(opt_pseudo_section.ede[0].purpose(), Some("Not Supported"));
1394        assert_eq!(opt_pseudo_section.ede[0].extra_text(), None);
1395    }
1396
1397    #[test]
1398    fn test_parse_as_query_message_with_ede_with_extra_text() {
1399        let raw_dns_message = "szgAAAABAAAAAAABAmg1B2V4YW1wbGUDY29tAAAGAAEAACkE0AEBQAAAOQAPADUACW5vIFNFUCBtYXRjaGluZyB0aGUgRFMgZm91bmQgZm9yIGRuc3NlYy1mYWlsZWQub3JnLg==";
1400        let raw_query_message = BASE64
1401            .decode(raw_dns_message.as_bytes())
1402            .expect("Invalid base64 encoded data.");
1403        let parse_result = DnsMessageParser::new(raw_query_message).parse_as_query_message();
1404        assert!(parse_result.is_ok());
1405        let message = parse_result.expect("Message is not parsed.");
1406        let opt_pseudo_section = message.opt_pseudo_section.expect("OPT section was missing");
1407        assert_eq!(opt_pseudo_section.ede.len(), 1);
1408        assert_eq!(opt_pseudo_section.ede[0].info_code(), 9u16);
1409        assert_eq!(opt_pseudo_section.ede[0].purpose(), Some("DNSKEY Missing"));
1410        assert_eq!(
1411            opt_pseudo_section.ede[0].extra_text(),
1412            Some("no SEP matching the DS found for dnssec-failed.org.".to_string())
1413        );
1414    }
1415
1416    #[test]
1417    fn test_parse_as_query_message_with_multiple_ede() {
1418        let raw_dns_message =
1419            "szgAAAABAAAAAAABAmg1B2V4YW1wbGUDY29tAAAGAAEAACkAAAEBQAAADAAPAAIAFQAPAAIAFA==";
1420        let raw_query_message = BASE64
1421            .decode(raw_dns_message.as_bytes())
1422            .expect("Invalid base64 encoded data.");
1423        let parse_result = DnsMessageParser::new(raw_query_message).parse_as_query_message();
1424        assert!(parse_result.is_ok());
1425        let message = parse_result.expect("Message is not parsed.");
1426        let opt_pseudo_section = message.opt_pseudo_section.expect("OPT section was missing");
1427        assert_eq!(opt_pseudo_section.ede.len(), 2);
1428        assert_eq!(opt_pseudo_section.ede[0].info_code(), 21u16);
1429        assert_eq!(opt_pseudo_section.ede[0].purpose(), Some("Not Supported"));
1430        assert_eq!(opt_pseudo_section.ede[0].extra_text(), None);
1431        assert_eq!(opt_pseudo_section.ede[1].info_code(), 20u16);
1432        assert_eq!(
1433            opt_pseudo_section.ede[1].purpose(),
1434            Some("Not Authoritative")
1435        );
1436        assert_eq!(opt_pseudo_section.ede[1].extra_text(), None);
1437    }
1438
1439    #[test]
1440    fn test_parse_as_query_message_with_invalid_data() {
1441        let err = DnsMessageParser::new(vec![1, 2, 3])
1442            .parse_as_query_message()
1443            .expect_err("Expected TrustDnsError.");
1444        match err {
1445            DnsMessageParserError::TrustDnsError { source: e } => {
1446                assert_eq!(
1447                    e.to_string(),
1448                    "decoding error: unexpected end of input reached"
1449                )
1450            }
1451            DnsMessageParserError::SimpleError { cause: e } => {
1452                panic!("Expected TrustDnsError, got {}.", &e)
1453            }
1454            _ => panic!("{err}."),
1455        }
1456    }
1457
1458    #[test]
1459    fn test_parse_as_query_message_with_unsupported_rdata() {
1460        let raw_query_message_base64 = "eEaFgAABAAEAAAAABGRvYTEHZXhhbXBsZQNjb20AAQMAAcAMAQMAAQAADhAAIAAAAAAAAAAAAgIiImh0dHBzOi8vd3d3LmlzYy5vcmcv";
1461        let raw_query_message = BASE64
1462            .decode(raw_query_message_base64.as_bytes())
1463            .expect("Invalid base64 encoded data.");
1464        let dns_query_message = DnsMessageParser::new(raw_query_message)
1465            .parse_as_query_message()
1466            .expect("Invalid DNS query message.");
1467        assert_eq!(dns_query_message.answer_section[0].rdata, None);
1468        assert_ne!(dns_query_message.answer_section[0].rdata_bytes, None);
1469    }
1470
1471    #[test]
1472    fn test_parse_response_with_https_rdata() {
1473        let raw_response_message_base64 = "Oe2BgAABAAEAAAABBGNkbnAHc2FuamFnaANjb20AAEEAAcAMAEEAAQAAASwAPQABAAABAAYCaDMCaDIABAAIrEDEHKxAxRwABgAgJgZHAADmAAAAAAAArEDEHCYGRwAA5gAAAAAAAKxAxRwAACkE0AAAAAAAHAAKABjWOVAgEGik/gEAAABlwiAuXkvEOviB1sk=";
1474        let raw_response_message = BASE64
1475            .decode(raw_response_message_base64.as_bytes())
1476            .expect("Invalid base64 encoded data.");
1477        let dns_response_message = DnsMessageParser::new(raw_response_message)
1478            .parse_as_query_message()
1479            .expect("Invalid DNS query message.");
1480        assert_eq!(
1481            dns_response_message.answer_section[0].rdata,
1482            Some(r#"1 . alpn="h3,h2" ipv4hint="172.64.196.28,172.64.197.28" ipv6hint="2606:4700:e6::ac40:c41c,2606:4700:e6::ac40:c51c""#.to_string())
1483        );
1484        assert_eq!(dns_response_message.answer_section[0].record_type_id, 65u16);
1485        assert_eq!(dns_response_message.answer_section[0].rdata_bytes, None);
1486    }
1487
1488    #[test]
1489    fn test_parse_response_with_hinfo_rdata() {
1490        let raw_response_message_base64 =
1491            "wS2BgAABAAEAAAAAB3RyYWNrZXIEZGxlcgNvcmcAAP8AAcAMAA0AAQAAC64ACQdSRkM4NDgyAA==";
1492        let raw_response_message = BASE64
1493            .decode(raw_response_message_base64.as_bytes())
1494            .expect("Invalid base64 encoded data.");
1495        let dns_response_message = DnsMessageParser::new(raw_response_message)
1496            .parse_as_query_message()
1497            .expect("Invalid DNS query message.");
1498        assert_eq!(
1499            dns_response_message.answer_section[0].rdata,
1500            Some(r#""RFC8482" """#.to_string())
1501        );
1502        assert_eq!(dns_response_message.answer_section[0].record_type_id, 13u16);
1503        assert_eq!(dns_response_message.answer_section[0].rdata_bytes, None);
1504    }
1505
1506    #[test]
1507    fn test_format_bytes_as_hex_string() {
1508        assert_eq!(
1509            "01.02.03.AB.CD.EF",
1510            &format_bytes_as_hex_string(&[1, 2, 3, 0xab, 0xcd, 0xef])
1511        );
1512    }
1513
1514    #[test]
1515    fn test_parse_unknown_record_type() {
1516        assert_eq!("A", parse_unknown_record_type(1).unwrap());
1517        assert_eq!("ANY", parse_unknown_record_type(255).unwrap());
1518        assert!(parse_unknown_record_type(22222).is_none());
1519    }
1520
1521    #[test]
1522    fn test_parse_as_update_message_failure() {
1523        let raw_dns_message = "ChVqYW1lcy1WaXJ0dWFsLU1hY2hpbmUSC0JJTkQgOS4xNi4zcq0ICAQQARgBIgSs\
1524        FDetKgTAN1MeMMn/Ajg1QL6m6fcFTQLEKhVS+QMSSIIAAAEAAAAFAAUJZmFjZWJvb2sxA2NvbQAAAQABwAwAAgABAAKjA\
1525        AAPA25zMQhyZW50b25kY8AWwAwAAgABAAKjAAAGA25zMsAvIENLMFBPSk1HODc0TEpSRUY3RUZOODQzMFFWSVQ4QlNNwB\
1526        YAMgABAAFRgAAjAQEAAAAUZQGgwlcg7hVvbE45Y2s62gMS2SoAByIAAAAAApDATAAuAAEAAVGAALcAMggCAAFRgF8ACGF\
1527        e9r15m6QDY29tAFOih16McCzogcR6RZIu3kqZa27Bo1jtfzwzDENJIZItSCRuLqRO6oA90sCLItOEQv0skpQKtJQXmTZU\
1528        nqe3XK+1t/Op8G9cmeMXgCvynTJmm0WouSv+SuwBOjgqCaNuWpwbiIcaXY/NlId1lPpl8LJyTIRtFqGifW0FnYFe/Lzs3\
1529        pfZLoKMAG4/8Upqqv4F+Ij1oue1C6KWe0hn+beIKkIgN0pLMjVFTUhITThBMDFNTzBBRVFRTjdHMlVQSjI4NjfAFgAyAA\
1530        EAAVGAACIBAQAAABQ86ELf24DH1kAfgQ4dyyuf0+6y5wAGIAAAAAASwCsAAQABAAKjAAAEbD0TCsArAAEAAQACowAABKx\
1531        iwCLARgABAAEAAqMAAAQuprYzwEYAAQABAAKjAAAEXXMcaAAAKRAAAACAAAAAWgUDY29tAGC+pun3BW2/LUQYcvkDEkiC\
1532        AAABAAAABQAFCWZhY2Vib29rMQNjb20AAAEAAcAMAAIAAQACowAADwNuczEIcmVudG9uZGPAFsAMAAIAAQACowAABgNuc\
1533        zLALyBDSzBQT0pNRzg3NExKUkVGN0VGTjg0MzBRVklUOEJTTcAWADIAAQABUYAAIwEBAAAAFGUBoMJXIO4Vb2xOOWNrOt\
1534        oDEtkqAAciAAAAAAKQwEwALgABAAFRgAC3ADIIAgABUYBfAAhhXva9eZukA2NvbQBToodejHAs6IHEekWSLt5KmWtuwaN\
1535        Y7X88MwxDSSGSLUgkbi6kTuqAPdLAiyLThEL9LJKUCrSUF5k2VJ6nt1yvtbfzqfBvXJnjF4Ar8p0yZptFqLkr/krsATo4\
1536        KgmjblqcG4iHGl2PzZSHdZT6ZfCyckyEbRahon1tBZ2BXvy87N6X2S6CjABuP/FKaqr+BfiI9aLntQuilntIZ/m3iCpCI\
1537        DdKSzI1RU1ISE04QTAxTU8wQUVRUU43RzJVUEoyODY3wBYAMgABAAFRgAAiAQEAAAAUPOhC39uAx9ZAH4EOHcsrn9Pusu\
1538        cABiAAAAAAEsArAAEAAQACowAABGw9EwrAKwABAAEAAqMAAASsYsAiwEYAAQABAAKjAAAELqa2M8BGAAEAAQACowAABF1\
1539        zHGgAACkQAAAAgAAAAHgB";
1540        let raw_update_message = BASE64
1541            .decode(raw_dns_message.as_bytes())
1542            .expect("Invalid base64 encoded data.");
1543        assert!(
1544            DnsMessageParser::new(raw_update_message)
1545                .parse_as_update_message()
1546                .is_err()
1547        );
1548    }
1549
1550    #[test]
1551    fn test_parse_bad_prefix_value() {
1552        // this testcase have prefix value of 160,
1553        let raw_dns_message = "oAAAMgABAAAAAAABAAABAAAAACYAAC8BAAAAAaAAAAAAAA==";
1554        let raw_query_message = BASE64
1555            .decode(raw_dns_message.as_bytes())
1556            .expect("Invalid base64 encoded data.");
1557        assert!(
1558            DnsMessageParser::new(raw_query_message)
1559                .parse_as_query_message()
1560                .is_err()
1561        );
1562    }
1563
1564    #[test]
1565    fn test_parse_as_update_message() {
1566        let raw_dns_message = "xjUoAAABAAAAAQAAB2V4YW1wbGUDY29tAAAGAAECaDXADAD/AP8AAAAAAAA=";
1567        let raw_update_message = BASE64
1568            .decode(raw_dns_message.as_bytes())
1569            .expect("Invalid base64 encoded data.");
1570        let parse_result = DnsMessageParser::new(raw_update_message).parse_as_update_message();
1571        assert!(parse_result.is_ok());
1572        let message = parse_result.expect("Message is not parsed.");
1573        assert_eq!(message.header.qr, 0);
1574        assert_eq!(message.update_section.len(), 1);
1575        assert_eq!(message.update_section.first().unwrap().class, "ANY");
1576        assert_eq!(&message.zone_to_update.zone_type.clone().unwrap(), "SOA");
1577        assert_eq!(message.zone_to_update.name, "example.com.");
1578    }
1579
1580    #[test]
1581    fn test_parse_loc_rdata_size() {
1582        let data: u8 = 51;
1583        let expected: f64 = 30.0;
1584        assert!((expected - parse_loc_rdata_size(data).unwrap()).abs() < f64::EPSILON);
1585
1586        let data: u8 = 22;
1587        let expected: f64 = 10000.0;
1588        assert!((expected - parse_loc_rdata_size(data).unwrap()).abs() < f64::EPSILON);
1589
1590        let data: u8 = 19;
1591        let expected: f64 = 10.0;
1592        assert!((expected - parse_loc_rdata_size(data).unwrap()).abs() < f64::EPSILON);
1593    }
1594
1595    #[test]
1596    fn test_parse_loc_rdata_coordinates() {
1597        let coordinates: u32 = 2299997648;
1598        let dir = "N";
1599        let expected = String::from("42 21 54.000 N");
1600        assert_eq!(expected, parse_loc_rdata_coordinates(coordinates, dir));
1601
1602        let coordinates: u32 = 1891505648;
1603        let dir = "W";
1604        let expected = String::from("71 6 18.000 W");
1605        assert_eq!(expected, parse_loc_rdata_coordinates(coordinates, dir));
1606    }
1607
1608    #[test]
1609    fn test_format_rdata_for_a_type() {
1610        let rdata = RData::A(Ipv4Addr::from_str("1.2.3.4").unwrap().into());
1611        let rdata_text = format_rdata(&rdata);
1612        assert!(rdata_text.is_ok());
1613        if let Ok((parsed, raw_rdata)) = rdata_text {
1614            assert!(raw_rdata.is_none());
1615            assert_eq!("1.2.3.4", parsed.unwrap());
1616        }
1617    }
1618
1619    #[test]
1620    fn test_format_rdata_for_aaaa_type() {
1621        let rdata = RData::AAAA(Ipv6Addr::from_str("2001::1234").unwrap().into());
1622        let rdata_text = format_rdata(&rdata);
1623        assert!(rdata_text.is_ok());
1624        if let Ok((parsed, raw_rdata)) = rdata_text {
1625            assert!(raw_rdata.is_none());
1626            assert_eq!("2001::1234", parsed.unwrap());
1627        }
1628    }
1629
1630    #[test]
1631    fn test_format_rdata_for_cname_type() {
1632        let rdata = RData::CNAME(hickory_proto::rr::rdata::CNAME(
1633            Name::from_str("www.example.com.").unwrap(),
1634        ));
1635        let rdata_text = format_rdata(&rdata);
1636        assert!(rdata_text.is_ok());
1637        if let Ok((parsed, raw_rdata)) = rdata_text {
1638            assert!(raw_rdata.is_none());
1639            assert_eq!("www.example.com.", parsed.unwrap());
1640        }
1641    }
1642
1643    #[test]
1644    fn test_format_rdata_for_cname_type_downcase() {
1645        let rdata = RData::CNAME(hickory_proto::rr::rdata::CNAME(
1646            Name::from_str("WWW.Example.Com.").unwrap(),
1647        ));
1648        let rdata_text = format_rdata_with_options(
1649            &rdata,
1650            DnsParserOptions {
1651                lowercase_hostnames: true,
1652            },
1653        );
1654        assert!(rdata_text.is_ok());
1655        if let Ok((parsed, raw_rdata)) = rdata_text {
1656            assert!(raw_rdata.is_none());
1657            assert_eq!("www.example.com.", parsed.unwrap());
1658        }
1659    }
1660
1661    #[test]
1662    fn test_format_rdata_for_txt_type() {
1663        let rdata = RData::TXT(TXT::new(vec![
1664            "abc\"def".to_string(),
1665            "gh\\i".to_string(),
1666            "".to_string(),
1667            "j".to_string(),
1668        ]));
1669        let rdata_text = format_rdata(&rdata);
1670        assert!(rdata_text.is_ok());
1671        if let Ok((parsed, raw_rdata)) = rdata_text {
1672            assert!(raw_rdata.is_none());
1673            assert_eq!(r#""abc\"def" "gh\\i" "" "j""#, parsed.unwrap());
1674        }
1675    }
1676
1677    #[test]
1678    fn test_format_rdata_for_caa_type() {
1679        let rdata1 = RData::CAA(CAA::new_issue(
1680            true,
1681            Some(Name::parse("example.com", None).unwrap()),
1682            vec![],
1683        ));
1684        let rdata2 = RData::CAA(CAA::new_issue(
1685            true,
1686            Some(Name::parse("example.com", None).unwrap()),
1687            vec![KeyValue::new("key", "value")],
1688        ));
1689        let rdata_text1 = format_rdata(&rdata1);
1690        let rdata_text2 = format_rdata(&rdata2);
1691
1692        assert!(rdata_text1.is_ok());
1693        assert!(rdata_text2.is_ok());
1694
1695        if let Ok((parsed, raw_rdata)) = rdata_text1 {
1696            assert!(raw_rdata.is_none());
1697            assert_eq!("1 issue \"example.com\"", parsed.unwrap());
1698        }
1699
1700        if let Ok((parsed, raw_rdata)) = rdata_text2 {
1701            assert!(raw_rdata.is_none());
1702            assert_eq!("1 issue \"example.com; key=value\"", parsed.unwrap());
1703        }
1704    }
1705
1706    #[test]
1707    fn test_format_rdata_for_tlsa_type() {
1708        let rdata = RData::TLSA(TLSA::new(
1709            CertUsage::PkixEe,
1710            Selector::Spki,
1711            Matching::Sha256,
1712            vec![1, 2, 3, 4, 5, 6, 7, 8],
1713        ));
1714        let rdata_text = format_rdata(&rdata);
1715        assert!(rdata_text.is_ok());
1716        if let Ok((parsed, raw_rdata)) = rdata_text {
1717            assert!(raw_rdata.is_none());
1718            assert_eq!("1 1 1 0102030405060708", parsed.unwrap());
1719        }
1720    }
1721
1722    #[test]
1723    fn test_format_rdata_for_sshfp_type() {
1724        let rdata = RData::SSHFP(SSHFP::new(
1725            Algorithm::ECDSA,
1726            FingerprintType::SHA1,
1727            vec![115, 115, 104, 102, 112],
1728        ));
1729        let rdata_text = format_rdata(&rdata);
1730        assert!(rdata_text.is_ok());
1731        if let Ok((parsed, raw_rdata)) = rdata_text {
1732            assert!(raw_rdata.is_none());
1733            assert_eq!("3 1 7373686670", parsed.unwrap());
1734        }
1735    }
1736
1737    #[test]
1738    fn test_format_rdata_for_naptr_type() {
1739        let rdata1 = RData::NAPTR(NAPTR::new(
1740            8,
1741            16,
1742            b"aa11AA-".to_vec().into_boxed_slice(),
1743            b"services".to_vec().into_boxed_slice(),
1744            b"regexpr".to_vec().into_boxed_slice(),
1745            Name::from_str("naptr.example.com").unwrap(),
1746        ));
1747        let rdata_text1 = format_rdata(&rdata1);
1748
1749        let rdata2 = RData::NAPTR(NAPTR::new(
1750            8,
1751            16,
1752            b"aa1\"\\1AA-".to_vec().into_boxed_slice(),
1753            b"\\services2\"".to_vec().into_boxed_slice(),
1754            b"re%ge\"xp.r\\".to_vec().into_boxed_slice(),
1755            Name::from_str("naptr.example.com").unwrap(),
1756        ));
1757        let rdata_text2 = format_rdata(&rdata2);
1758
1759        assert!(rdata_text1.is_ok());
1760        assert!(rdata_text2.is_ok());
1761
1762        if let Ok((parsed, raw_rdata)) = rdata_text1 {
1763            assert!(raw_rdata.is_none());
1764            assert_eq!(
1765                "8 16 \"aa11AA-\" \"services\" \"regexpr\" naptr.example.com",
1766                parsed.unwrap()
1767            );
1768        }
1769
1770        if let Ok((parsed, raw_rdata)) = rdata_text2 {
1771            assert!(raw_rdata.is_none());
1772            assert_eq!(
1773                "8 16 \"aa1\\\"\\\\1AA-\" \"\\\\services2\\\"\" \"re%ge\\\"xp.r\\\\\" naptr.example.com",
1774                parsed.unwrap()
1775            );
1776        }
1777    }
1778
1779    #[test]
1780    fn test_format_rdata_for_dnskey_type() {
1781        let rdata1 = RData::DNSSEC(DNSSECRData::DNSKEY(DNSKEY::new(
1782            true,
1783            true,
1784            false,
1785            PublicKeyBuf::new(vec![0, 1, 2, 3, 4, 5, 6, 7], DNSSEC_Algorithm::RSASHA256),
1786        )));
1787        let rdata_text1 = format_rdata(&rdata1);
1788
1789        let rdata2 = RData::DNSSEC(DNSSECRData::DNSKEY(DNSKEY::new(
1790            true,
1791            false,
1792            false,
1793            PublicKeyBuf::new(vec![0, 1, 2, 3, 4, 5, 6, 7], DNSSEC_Algorithm::RSASHA256),
1794        )));
1795        let rdata_text2 = format_rdata(&rdata2);
1796
1797        let rdata3 = RData::DNSSEC(DNSSECRData::DNSKEY(DNSKEY::new(
1798            true,
1799            true,
1800            true,
1801            PublicKeyBuf::new(vec![0, 1, 2, 3, 4, 5, 6, 7], DNSSEC_Algorithm::RSASHA256),
1802        )));
1803        let rdata_text3 = format_rdata(&rdata3);
1804
1805        assert!(rdata_text1.is_ok());
1806        assert!(rdata_text2.is_ok());
1807        assert!(rdata_text3.is_ok());
1808
1809        if let Ok((parsed, raw_rdata)) = rdata_text1 {
1810            assert!(raw_rdata.is_none());
1811            assert_eq!("257 3 8 AAECAwQFBgc=", parsed.unwrap());
1812        }
1813        if let Ok((parsed, raw_rdata)) = rdata_text2 {
1814            assert!(raw_rdata.is_none());
1815            assert_eq!("256 3 8 AAECAwQFBgc=", parsed.unwrap());
1816        }
1817        if let Ok((parsed, raw_rdata)) = rdata_text3 {
1818            assert!(raw_rdata.is_none());
1819            assert_eq!("0 3 8 AAECAwQFBgc=", parsed.unwrap());
1820        }
1821    }
1822
1823    #[test]
1824    fn test_format_rdata_for_nsec_type() {
1825        let rdata = RData::DNSSEC(DNSSECRData::NSEC(NSEC::new(
1826            Name::from_str("www.example.com").unwrap(),
1827            vec![RecordType::A, RecordType::AAAA],
1828        )));
1829        let rdata_text = format_rdata(&rdata);
1830        assert!(rdata_text.is_ok());
1831        if let Ok((parsed, raw_rdata)) = rdata_text {
1832            assert!(raw_rdata.is_none());
1833            assert_eq!("www.example.com A AAAA", parsed.unwrap());
1834        }
1835    }
1836
1837    #[test]
1838    fn test_format_rdata_for_nsec3_type() {
1839        let rdata = RData::DNSSEC(DNSSECRData::NSEC3(NSEC3::new(
1840            Nsec3HashAlgorithm::SHA1,
1841            true,
1842            2,
1843            vec![1, 2, 3, 4, 5],
1844            vec![6, 7, 8, 9, 0],
1845            vec![RecordType::A, RecordType::AAAA],
1846        )));
1847        let rdata_text = format_rdata(&rdata);
1848        assert!(rdata_text.is_ok());
1849        if let Ok((parsed, raw_rdata)) = rdata_text {
1850            assert!(raw_rdata.is_none());
1851            assert_eq!("1 1 2 0102030405 0O3GG280 A AAAA", parsed.unwrap());
1852        }
1853    }
1854
1855    #[test]
1856    fn test_format_rdata_for_nsec3param_type() {
1857        let rdata = RData::DNSSEC(DNSSECRData::NSEC3PARAM(NSEC3PARAM::new(
1858            Nsec3HashAlgorithm::SHA1,
1859            true,
1860            2,
1861            vec![1, 2, 3, 4, 5],
1862        )));
1863        let rdata_text = format_rdata(&rdata);
1864        assert!(rdata_text.is_ok());
1865        if let Ok((parsed, raw_rdata)) = rdata_text {
1866            assert!(raw_rdata.is_none());
1867            assert_eq!("1 1 2 0102030405", parsed.unwrap());
1868        }
1869    }
1870
1871    #[test]
1872    fn test_format_rdata_for_sig_type() {
1873        // SIG wire: type_covered=NULL(10), alg=8, labels=0, orig_ttl=0, expire=2, inception=1,
1874        // keytag=5, signer=www.example.com, sig=[0..=31]
1875        let mut wire: Vec<u8> = Vec::new();
1876        wire.extend_from_slice(&10u16.to_be_bytes());
1877        wire.push(8u8);
1878        wire.push(0u8);
1879        wire.extend_from_slice(&0u32.to_be_bytes());
1880        wire.extend_from_slice(&2u32.to_be_bytes());
1881        wire.extend_from_slice(&1u32.to_be_bytes());
1882        wire.extend_from_slice(&5u16.to_be_bytes());
1883        wire.extend_from_slice(&[
1884            3, b'w', b'w', b'w', 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c', b'o', b'm',
1885            0,
1886        ]);
1887        wire.extend(0u8..=31);
1888        test_format_rdata(
1889            &BASE64.encode(&wire),
1890            24,
1891            "NULL 8 0 0 2 1 5 www.example.com. AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
1892        );
1893    }
1894
1895    #[test]
1896    fn test_format_rdata_for_key_type() {
1897        let rdata = RData::DNSSEC(DNSSECRData::KEY(KEY::new(
1898            KeyTrust::NotPrivate,
1899            KeyUsage::Host,
1900            #[allow(deprecated)]
1901            UpdateScope {
1902                zone: false,
1903                strong: false,
1904                unique: true,
1905                general: true,
1906            },
1907            Protocol::DNSSEC,
1908            DNSSEC_Algorithm::RSASHA256,
1909            vec![
1910                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1911                23, 24, 25, 26, 27, 28, 29, 29, 31,
1912            ],
1913        )));
1914        let rdata_text = format_rdata(&rdata);
1915        assert!(rdata_text.is_ok());
1916        if let Ok((parsed, raw_rdata)) = rdata_text {
1917            assert!(raw_rdata.is_none());
1918            assert_eq!(
1919                "16387 3 8 AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHR8=",
1920                parsed.unwrap()
1921            );
1922        }
1923    }
1924
1925    // rsig is a derivation of the SIG record data, but the upstream crate does not handle that with an trait
1926    // so there isn't really a great way to reduce code duplication here.
1927    #[test]
1928    fn test_format_rdata_for_rsig_type() {
1929        let input = SigInput {
1930            type_covered: RecordType::NULL,
1931            algorithm: DNSSEC_Algorithm::RSASHA256,
1932            num_labels: 0,
1933            original_ttl: 0,
1934            sig_expiration: SerialNumber::new(2),
1935            sig_inception: SerialNumber::new(1),
1936            key_tag: 5,
1937            signer_name: Name::from_str("www.example.com").unwrap(),
1938        };
1939        let rdata = RData::DNSSEC(DNSSECRData::RRSIG(RRSIG::from_sig(
1940            input,
1941            (0u8..=31).collect(),
1942        )));
1943        let rdata_text = format_rdata(&rdata);
1944        assert!(rdata_text.is_ok());
1945        if let Ok((parsed, raw_rdata)) = rdata_text {
1946            assert!(raw_rdata.is_none());
1947            assert_eq!(
1948                "NULL 8 0 0 2 1 5 www.example.com AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
1949                parsed.unwrap()
1950            );
1951        }
1952    }
1953
1954    #[test]
1955    fn test_format_rdata_for_ds_type() {
1956        let rdata = RData::DNSSEC(DNSSECRData::DS(DS::new(
1957            0xF00F,
1958            DNSSEC_Algorithm::RSASHA256,
1959            DigestType::SHA256,
1960            vec![5, 6, 7, 8],
1961        )));
1962        let rdata_text = format_rdata(&rdata);
1963        assert!(rdata_text.is_ok());
1964        if let Ok((parsed, raw_rdata)) = rdata_text {
1965            assert!(raw_rdata.is_none());
1966            assert_eq!("61455 8 2 05060708", parsed.unwrap());
1967        }
1968    }
1969
1970    #[test]
1971    fn test_format_rdata_for_svcb_type() {
1972        let rdata = RData::SVCB(svcb::SVCB::new(
1973            1,
1974            Name::root(),
1975            vec![
1976                (
1977                    svcb::SvcParamKey::Alpn,
1978                    svcb::SvcParamValue::Alpn(svcb::Alpn(vec!["h3".to_string(), "h2".to_string()])),
1979                ),
1980                (
1981                    svcb::SvcParamKey::Ipv4Hint,
1982                    svcb::SvcParamValue::Ipv4Hint(svcb::IpHint(vec![
1983                        A(Ipv4Addr::new(104, 18, 36, 155)),
1984                        A(Ipv4Addr::new(172, 64, 151, 101)),
1985                    ])),
1986                ),
1987            ],
1988        ));
1989        let rdata_text = format_rdata(&rdata);
1990        assert!(rdata_text.is_ok());
1991        if let Ok((parsed, raw_rdata)) = rdata_text {
1992            assert!(raw_rdata.is_none());
1993            assert_eq!(
1994                r#"1 . alpn="h3,h2" ipv4hint="104.18.36.155,172.64.151.101""#,
1995                parsed.unwrap()
1996            );
1997        }
1998    }
1999
2000    #[test]
2001    fn test_format_rdata_for_https_type() {
2002        let rdata = RData::HTTPS(HTTPS(svcb::SVCB::new(
2003            1,
2004            Name::root(),
2005            vec![
2006                (
2007                    svcb::SvcParamKey::Alpn,
2008                    svcb::SvcParamValue::Alpn(svcb::Alpn(vec!["h3".to_string(), "h2".to_string()])),
2009                ),
2010                (
2011                    svcb::SvcParamKey::Ipv4Hint,
2012                    svcb::SvcParamValue::Ipv4Hint(svcb::IpHint(vec![
2013                        A(Ipv4Addr::new(104, 18, 36, 155)),
2014                        A(Ipv4Addr::new(172, 64, 151, 101)),
2015                    ])),
2016                ),
2017            ],
2018        )));
2019        let rdata_text = format_rdata(&rdata);
2020        assert!(rdata_text.is_ok());
2021        if let Ok((parsed, raw_rdata)) = rdata_text {
2022            assert!(raw_rdata.is_none());
2023            assert_eq!(
2024                r#"1 . alpn="h3,h2" ipv4hint="104.18.36.155,172.64.151.101""#,
2025                parsed.unwrap()
2026            );
2027        }
2028    }
2029
2030    #[test]
2031    fn test_format_rdata_for_hinfo_type() {
2032        let rdata = RData::HINFO(HINFO::new("intel".to_string(), "linux".to_string()));
2033        let rdata_text = format_rdata(&rdata);
2034        assert!(rdata_text.is_ok());
2035        if let Ok((parsed, raw_rdata)) = rdata_text {
2036            assert!(raw_rdata.is_none());
2037            assert_eq!(r#""intel" "linux""#, parsed.unwrap());
2038        }
2039    }
2040
2041    #[test]
2042    fn test_format_rdata_for_csync_type() {
2043        let types = vec![RecordType::A, RecordType::NS, RecordType::AAAA];
2044        let rdata = RData::CSYNC(CSYNC::new(123, true, true, types));
2045        let rdata_text = format_rdata(&rdata);
2046        assert!(rdata_text.is_ok());
2047        if let Ok((parsed, raw_rdata)) = rdata_text {
2048            assert!(raw_rdata.is_none());
2049            assert_eq!("123 3 A NS AAAA", parsed.unwrap());
2050        }
2051    }
2052
2053    #[test]
2054    fn test_format_rdata_for_opt_type() {
2055        let options = vec![(
2056            EdnsCode::LLQ,
2057            EdnsOption::Unknown(u16::from(EdnsCode::LLQ), vec![0x01; 18]),
2058        )];
2059        let rdata = RData::OPT(OPT::new(options));
2060        let rdata_text = format_rdata(&rdata);
2061        assert!(rdata_text.is_ok());
2062        if let Ok((parsed, raw_rdata)) = rdata_text {
2063            assert!(raw_rdata.is_none());
2064            assert_eq!("LLQ=AQEBAQEBAQEBAQEBAQEBAQEB", parsed.unwrap());
2065        }
2066    }
2067
2068    #[test]
2069    fn test_format_rdata_for_cert_type() {
2070        let rdata = RData::CERT(CERT::new(
2071            CertType::Experimental(65534),
2072            65535,
2073            CertAlgorithm::RSASHA1,
2074            BASE64
2075                .decode(
2076                    b"MxFcby9k/yvedMfQgKzhH5er0Mu/vILz4\
2077                5IkskceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY=",
2078                )
2079                .unwrap(),
2080        ));
2081        let rdata_text = format_rdata(&rdata);
2082        assert!(rdata_text.is_ok());
2083        if let Ok((parsed, raw_rdata)) = rdata_text {
2084            assert!(raw_rdata.is_none());
2085            assert_eq!(
2086                "65534 65535 RSASHA1 MxFcby9k/yvedMfQgKzhH5er0Mu/vILz4\
2087                5IkskceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY=",
2088                parsed.unwrap()
2089            );
2090        }
2091    }
2092
2093    #[test]
2094    fn test_format_rdata_for_minfo_type() {
2095        test_format_rdata_with_compressed_domain_names(
2096            "5ZWBgAABAAEAAAABBm1pbmZvbwhleGFtcGxlMQNjb20AAA4AAcAMAA4AAQAADGsADQRmcmVkwBMDam9lwBMAACkQAAAAAAAAHAAKABgZ5zwJEK3VJQEAAABfSBqpS2bKf9CNBXg=",
2097            "BGZyZWTAEwNqb2XAEw==",
2098            14,
2099            "fred.example1.com. joe.example1.com.",
2100        );
2101    }
2102
2103    #[test]
2104    fn test_format_rdata_for_mb_type() {
2105        test_format_rdata_with_compressed_domain_names(
2106            "t8eBgAABAAEAAAABAm1iCGV4YW1wbGUxA2NvbQAABwABwAwABwABAAAA5AAJBmFhYmJjY8APAA\
2107            ApEAAAAAAAABwACgAYedbJkVVpMhsBAAAAX0U+y6UJQtCd0MuPBmFhYmJjY8AP",
2108            "BmFhYmJjY8AP",
2109            7,
2110            "aabbcc.example1.com.",
2111        );
2112    }
2113
2114    #[test]
2115    fn test_format_rdata_for_mg_type() {
2116        test_format_rdata_with_compressed_domain_names(
2117            "o8ABIAABAAAAAAABAm1nCGV4YW1wbGUxA2NvbQAACAABAAApEAAAAAAAAAwACgAICQ3LVdp9euQ=",
2118            "wAw=",
2119            8,
2120            "mg.example1.com.",
2121        );
2122    }
2123
2124    #[test]
2125    fn test_format_rdata_for_mr_type() {
2126        test_format_rdata_with_compressed_domain_names(
2127            "VWQBIAABAAAAAAABAm1yCGV4YW1wbGUxA2NvbQAACQABAAApEAAAAAAAAAwACgAIaPayFPJ4rmY=",
2128            "wAw=",
2129            9,
2130            "mr.example1.com.",
2131        );
2132    }
2133
2134    #[test]
2135    fn test_format_rdata_for_wks_type() {
2136        test_format_rdata("gAgBDgYAAAFA", 11, "128.8.1.14 6 23 25");
2137
2138        test_format_rdata("gAgBDgYAAAE=", 11, "128.8.1.14 6 23");
2139    }
2140
2141    #[test]
2142    fn test_format_rdata_for_rp_type() {
2143        test_format_rdata_with_compressed_domain_names(
2144            "Xc0BIAABAAAAAAABAnJwCGV4YW1wbGUxA2NvbQAAEQABAAApEAAAAAAAAAwACgAIMoUjsVrqjwo=",
2145            "BWxvdWllB3RyYW50b3IDdW1kA2VkdQAETEFNMQZwZW9wbGUDdW1kA2VkdQA=",
2146            17,
2147            "louie.trantor.umd.edu. LAM1.people.umd.edu.",
2148        );
2149    }
2150
2151    #[test]
2152    fn test_format_rdata_for_afsdb_type() {
2153        test_format_rdata_with_compressed_domain_names(
2154            "uaMBIAABAAAAAAABBWFmc2RiCGV4YW1wbGUxA2NvbQAAEgABAAApEAAAAAAAAAwACgAINy\
2155            n/qwKTyVc=",
2156            "AAEHYmlnYmlyZAd0b2FzdGVyA2NvbQA=",
2157            18,
2158            "1 bigbird.toaster.com.",
2159        );
2160    }
2161
2162    #[test]
2163    fn test_format_rdata_for_x25_type() {
2164        test_format_rdata("DDMxMTA2MTcwMDk1Ng==", 19, "\"311061700956\"");
2165    }
2166
2167    #[test]
2168    fn test_format_rdata_for_isdn_type() {
2169        test_format_rdata("DzE1MDg2MjAyODAwMzIxNw==", 20, "\"150862028003217\"");
2170    }
2171
2172    #[test]
2173    fn test_format_rdata_for_rt_type() {
2174        test_format_rdata_with_compressed_domain_names(
2175            "K1cBEAABAAAAAAABAnJ0CGV4YW1wbGUxA2NvbQAAFQABAAApAgAAAIAAABwACgAY4Rzxu\
2176            TfOxRwNw0bSX0VXy7WIF30GJ7DD",
2177            "AAoCYWEHZXhhbXBsZQNjb20A",
2178            21,
2179            "10 aa.example.com.",
2180        );
2181    }
2182
2183    #[test]
2184    fn test_format_rdata_for_nsap_type() {
2185        test_format_rdata(
2186            "RwAFgABaAAAAAAHhM////wABYQA=",
2187            22,
2188            "0x47000580005A0000000001E133FFFFFF00016100",
2189        );
2190    }
2191
2192    #[test]
2193    fn test_format_rdata_for_px_type() {
2194        test_format_rdata_with_compressed_domain_names(
2195            "QF+BgAABAAEAAAABAnB4CGV4YW1wbGUxA2NvbQAAGgABwAwAGgABAAAOEAAlAAoEbmV0\
2196            MgJpdAAJUFJNRC1uZXQyCUFETUQtcDQwMARDLWl0AAAAKRAAAAAAAAAcAAoAGDnSHBrTcxU1AQAAAF9FWK\
2197            fIBBM9awy20w==",
2198            "AAoEbmV0MgJpdAAJUFJNRC1uZXQyCUFETUQtcDQwMARDLWl0AA==",
2199            26,
2200            "10 net2.it. PRMD-net2.ADMD-p400.C-it.",
2201        );
2202    }
2203
2204    #[test]
2205    fn test_format_rdata_for_loc_type() {
2206        test_format_rdata(
2207            "ADMWE4kXLdBwvhXwAJiNIA==",
2208            29,
2209            "42 21 54.000 N 71 6 18.000 W -24.00m 30m 10000m 10m",
2210        );
2211    }
2212
2213    #[test]
2214    fn test_format_rdata_for_kx_type() {
2215        test_format_rdata_with_compressed_domain_names(
2216            "E4yBgAABAAEAAAABAmt4CGV4YW1wbGUxA2NvbQAAJAABwAwAJAABAAAOEAASAAoCYWEHZ\
2217            XhhbXBsZQNjb20AAAApEAAAAAAAABwACgAYohY6RsSf9dsBAAAAX0VY5DfEoTM1iq9G",
2218            "AAoCYWEHZXhhbXBsZQNjb20A",
2219            36,
2220            "10 aa.example.com.",
2221        );
2222    }
2223
2224    #[test]
2225    fn test_format_rdata_for_a6_type() {
2226        test_format_rdata(
2227            "QBI0VniavN7wCFNVQk5FVC0xA0lQNghleGFtcGxlMQNjb20A",
2228            38,
2229            "64 ::1234:5678:9abc:def0 SUBNET-1.IP6.example1.com.",
2230        );
2231    }
2232
2233    #[test]
2234    fn test_format_rdata_for_sink_type() {
2235        test_format_rdata("AQIDdddd", 40, "1 2 3 dddd");
2236    }
2237
2238    #[test]
2239    fn test_format_rdata_for_apl_type() {
2240        test_format_rdata(
2241            "AAEVA8CoIAABHIPAqCY=",
2242            42,
2243            "1:192.168.32.0/21 !1:192.168.38.0/28",
2244        );
2245
2246        test_format_rdata("AAEEAeAAAggB/w==", 42, "1:224.0.0.0/4 2:ff00::/8");
2247
2248        test_format_rdata(
2249            "AAEVA8CoIAABHATAqCYsAAEdA8AAJgABHYPAACYAAR2EwAAmCA==",
2250            42,
2251            "1:192.168.32.0/21 1:192.168.38.44/28 \
2252            1:192.0.38.0/29 !1:192.0.38.0/29 !1:192.0.38.8/29",
2253        );
2254
2255        test_format_rdata(
2256            "AAEVA8CoIAABHATAqCYsAAEdA8AAJg==",
2257            42,
2258            "1:192.168.32.0/21 1:192.168.38.44/28 1:192.0.38.0/29",
2259        );
2260    }
2261
2262    #[test]
2263    fn test_format_rdata_for_dhcid_type() {
2264        test_format_rdata(
2265            "AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA=",
2266            49,
2267            "AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA=",
2268        );
2269    }
2270
2271    #[test]
2272    fn test_format_rdata_for_spf_type() {
2273        test_format_rdata(
2274            "BnY9c3BmMQMrbXgVYTpjb2xvLmV4YW1wbGUuY29tLzI4BC1hbGw=",
2275            99,
2276            "\"v=spf1\" \"+mx\" \"a:colo.example.com/28\" \"-all\"",
2277        );
2278    }
2279
2280    fn test_format_rdata(raw_data: &str, code: u16, expected_output: &str) {
2281        let raw_rdata = BASE64
2282            .decode(raw_data.as_bytes())
2283            .expect("Invalid base64 encoded rdata.");
2284        let mut decoder = BinDecoder::new(&raw_rdata);
2285        let record = Record::from_rdata(
2286            Name::new(),
2287            1,
2288            RData::read(
2289                &mut decoder,
2290                RecordType::from(code),
2291                Restrict::new(raw_rdata.len() as u16),
2292            )
2293            .unwrap(),
2294        );
2295        let rdata_text = DnsMessageParser::new(Vec::<u8>::new())
2296            .parse_dns_record(&record)
2297            .map(|r| r.rdata);
2298        assert!(rdata_text.is_ok());
2299        assert_eq!(expected_output, rdata_text.unwrap().unwrap());
2300    }
2301
2302    fn test_format_rdata_with_compressed_domain_names(
2303        raw_message: &str,
2304        raw_data_encoded: &str,
2305        code: u16,
2306        expected_output: &str,
2307    ) {
2308        let raw_message = BASE64
2309            .decode(raw_message.as_bytes())
2310            .expect("Invalid base64 encoded raw message.");
2311        let raw_message_len = raw_message.len();
2312        let mut message_parser = DnsMessageParser::new(raw_message);
2313        let raw_rdata = BASE64
2314            .decode(raw_data_encoded.as_bytes())
2315            .expect("Invalid base64 encoded raw rdata.");
2316        for i in 1..=2 {
2317            let record_rdata = NULL::with(raw_rdata.clone());
2318            let rdata_text = message_parser.format_unknown_rdata(code, &record_rdata);
2319            assert!(rdata_text.is_ok());
2320            assert_eq!(expected_output, rdata_text.unwrap().0.unwrap());
2321            assert_eq!(
2322                raw_message_len + i * raw_rdata.len(),
2323                message_parser
2324                    .raw_message_for_rdata_parsing()
2325                    .unwrap()
2326                    .len()
2327            );
2328        }
2329    }
2330}