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