Skip to main content

erbium/dns/
dnspkt.rs

1/*   Copyright 2024 Perry Lorier
2 *
3 *  Licensed under the Apache License, Version 2.0 (the "License");
4 *  you may not use this file except in compliance with the License.
5 *  You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 *  Unless required by applicable law or agreed to in writing, software
10 *  distributed under the License is distributed on an "AS IS" BASIS,
11 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 *  See the License for the specific language governing permissions and
13 *  limitations under the License.
14 *
15 *  SPDX-License-Identifier: Apache-2.0
16 *
17 *  Datastructures and serialisation of DNS packets.
18 */
19
20#[cfg(fuzzing)]
21use arbitrary::Arbitrary;
22use std::fmt;
23
24#[derive(Eq, Ord, PartialOrd, PartialEq, Clone, Copy)]
25#[cfg_attr(fuzzing, derive(Arbitrary))]
26pub struct Class(pub u16);
27
28pub const CLASS_IN: Class = Class(1); /* Internet */
29pub const CLASS_CH: Class = Class(3); /* ChaosNet */
30
31impl fmt::Display for Class {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            &CLASS_IN => write!(f, "IN"),
35            &CLASS_CH => write!(f, "CH"),
36            Class(x) => write!(f, "Class#{}", x),
37        }
38    }
39}
40
41impl fmt::Debug for Class {
42    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43        write!(f, "Class({})", self)
44    }
45}
46
47#[derive(Ord, PartialOrd, PartialEq, Eq, Clone, Hash, Copy)]
48#[cfg_attr(fuzzing, derive(Arbitrary))]
49pub struct Type(pub u16);
50
51pub const RR_A: Type = Type(1);
52pub const RR_NS: Type = Type(2);
53pub const RR_CNAME: Type = Type(5);
54pub const RR_SOA: Type = Type(6);
55pub const RR_PTR: Type = Type(12);
56pub const RR_MX: Type = Type(15);
57pub const RR_RP: Type = Type(17);
58pub const RR_AFSDB: Type = Type(18);
59pub const RR_RT: Type = Type(21);
60pub const RR_NAPTR: Type = Type(35);
61pub const RR_OPT: Type = Type(41);
62pub const RR_NSEC: Type = Type(47);
63pub const RR_NSEC3: Type = Type(50);
64pub const RR_ANY: Type = Type(255);
65
66impl fmt::Display for Type {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        match self {
69            &RR_A => write!(f, "A"),
70            &RR_NS => write!(f, "NS"),
71            &RR_CNAME => write!(f, "CNAME"),
72            &RR_SOA => write!(f, "SOA"),
73            &RR_PTR => write!(f, "PTR"),
74            &RR_NAPTR => write!(f, "NAPTR"),
75            &RR_OPT => write!(f, "OPT"),
76            &RR_NSEC => write!(f, "NSEC"),
77            &RR_NSEC3 => write!(f, "NSEC3"),
78            Type(x) => write!(f, "Type#{}", x),
79        }
80    }
81}
82
83impl fmt::Debug for Type {
84    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        write!(f, "Type({})", self)
86    }
87}
88
89#[derive(Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
90#[cfg_attr(fuzzing, derive(Arbitrary))]
91pub struct RCode(pub u16);
92pub const NOERROR: RCode = RCode(0);
93pub const FORMERR: RCode = RCode(1);
94pub const SERVFAIL: RCode = RCode(2);
95pub const NXDOMAIN: RCode = RCode(3);
96pub const NOTIMP: RCode = RCode(4);
97pub const REFUSED: RCode = RCode(5);
98pub const YXDOMAIN: RCode = RCode(6);
99pub const YXRRSET: RCode = RCode(7);
100pub const NXRRSET: RCode = RCode(8);
101pub const NOTAUTH: RCode = RCode(9);
102pub const NOTZONE: RCode = RCode(10);
103pub const DSOTYPENI: RCode = RCode(11);
104pub const BADVERS: RCode = RCode(16);
105pub const BADSIG: RCode = RCode(16); /* Yes, this is a dupe */
106pub const BADKEY: RCode = RCode(17);
107pub const BADTIME: RCode = RCode(18);
108pub const BADMODE: RCode = RCode(19);
109pub const BADNAME: RCode = RCode(20);
110pub const BADALG: RCode = RCode(21);
111pub const BADTRUNC: RCode = RCode(22);
112pub const BADCOOKIE: RCode = RCode(23);
113
114impl fmt::Display for RCode {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        match self {
117            &NOERROR => write!(f, "NOERROR"),
118            &FORMERR => write!(f, "FORMERR"),
119            &SERVFAIL => write!(f, "SERVFAIL"),
120            &NXDOMAIN => write!(f, "NXDOMAIN"),
121            &NOTIMP => write!(f, "NOTIMP"),
122            &REFUSED => write!(f, "REFUSED"),
123            &YXDOMAIN => write!(f, "YXDOMAIN"),
124            &YXRRSET => write!(f, "YXRRSET"),
125            &NXRRSET => write!(f, "NXRRSET"),
126            &NOTAUTH => write!(f, "NOTAUTH"),
127            &NOTZONE => write!(f, "NOTZONE"),
128            &DSOTYPENI => write!(f, "DSOTYPENI"),
129            &BADVERS => write!(f, "BADVERS/BADSIG"),
130            &BADKEY => write!(f, "BADKEY"),
131            &BADTIME => write!(f, "BADTIME"),
132            &BADMODE => write!(f, "BADMODE"),
133            &BADNAME => write!(f, "BADNAME"),
134            &BADALG => write!(f, "BADALG"),
135            &BADTRUNC => write!(f, "BADTRUNC"),
136            &BADCOOKIE => write!(f, "BADCOOKIE"),
137            RCode(x) => write!(f, "RCode#{}", x),
138        }
139    }
140}
141
142impl fmt::Debug for RCode {
143    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144        write!(f, "RCode({})", self)
145    }
146}
147
148fn display_byte(b: u8) -> String {
149    match b {
150        n @ 32..=127 => char::from(n).to_string(),
151        n => format!("\\{}", n),
152    }
153}
154
155#[derive(Ord, Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
156pub struct Label(Vec<u8>);
157
158impl From<Vec<u8>> for Label {
159    fn from(mut v: Vec<u8>) -> Self {
160        assert!(!v.is_empty());
161        v.shrink_to_fit();
162        Label(v)
163    }
164}
165impl fmt::Display for Label {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        write!(
168            f,
169            "{}",
170            self.0.iter().map(|&b| display_byte(b)).collect::<String>()
171        )
172    }
173}
174
175#[cfg(fuzzing)]
176impl<'a> Arbitrary<'a> for Label {
177    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
178        /* Labels cannot be empty. */
179        loop {
180            let v = Vec::<u8>::arbitrary(u)?;
181            if v.len() > 0 && v.len() < 64 {
182                return Ok(Self(v));
183            }
184        }
185    }
186}
187
188#[derive(Clone, PartialEq, Eq, PartialOrd, Hash)]
189#[cfg_attr(fuzzing, derive(Arbitrary))]
190pub struct Domain(Vec<Label>);
191
192impl Domain {
193    pub fn ends_with(&self, other: &Self) -> bool {
194        self.0.ends_with(&other.0)
195    }
196}
197
198impl From<Vec<Label>> for Domain {
199    fn from(mut v: Vec<Label>) -> Self {
200        v.shrink_to_fit();
201        Domain(v)
202    }
203}
204
205impl fmt::Display for Domain {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207        write!(
208            f,
209            "{}",
210            self.0
211                .iter()
212                .map(|x| x.to_string())
213                .collect::<Vec<String>>()
214                .join(".")
215        )
216    }
217}
218
219impl fmt::Debug for Domain {
220    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221        write!(f, "Domain({:?})", self.0)
222    }
223}
224
225impl std::str::FromStr for Domain {
226    type Err = &'static str;
227    fn from_str(s: &str) -> Result<Self, Self::Err> {
228        let mut v = vec![];
229        let mut l = vec![];
230        for c in s.chars() {
231            match c {
232                '\\' => return Err("\\ not yet supported"), // TODO
233                '.' => {
234                    if l.is_empty() {
235                        return Err("illegal empty label");
236                    }
237                    l.shrink_to_fit();
238                    v.push(Label(l));
239                    l = vec![]
240                }
241                ch if ch.is_ascii() => l.push(ch as u8),
242                _ => return Err("illegal charactor in label"),
243            }
244        }
245        if !l.is_empty() {
246            l.shrink_to_fit();
247            v.push(Label(l));
248        }
249        v.shrink_to_fit();
250        Ok(Domain(v))
251    }
252}
253
254// We want to sort longer suffixes first.
255pub fn compare_longest_suffix(lhs: &Domain, rhs: &Domain) -> std::cmp::Ordering {
256    use std::cmp::Ordering::*;
257    if lhs.0.len() != rhs.0.len() {
258        if lhs.0.len() < rhs.0.len() {
259            Greater // Because we want the largest first, not smallest first.
260        } else {
261            Less
262        }
263    } else {
264        // If they are the same length, then just compare based on the text
265        lhs.0.cmp(&rhs.0)
266    }
267}
268
269#[derive(Clone, Eq, PartialEq)]
270#[cfg_attr(fuzzing, derive(Arbitrary))]
271pub struct Question {
272    pub qdomain: Domain,
273    pub qclass: Class,
274    pub qtype: Type,
275}
276
277impl fmt::Display for Question {
278    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279        write!(f, "{:?} {:?} {:?}", self.qdomain, self.qclass, self.qtype)
280    }
281}
282
283impl fmt::Debug for Question {
284    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
285        write!(
286            f,
287            "Question({:?} {:?} {:?})",
288            self.qdomain, self.qclass, self.qtype
289        )
290    }
291}
292
293#[derive(Ord, Eq, PartialEq, PartialOrd, Clone)]
294#[cfg_attr(fuzzing, derive(Arbitrary))]
295pub struct EdnsCode(pub u16);
296
297pub const EDNS_NSID: EdnsCode = EdnsCode(3);
298pub const EDNS_CLIENT_SUBNET: EdnsCode = EdnsCode(8);
299pub const EDNS_COOKIE: EdnsCode = EdnsCode(10);
300pub const EDNS_EDE: EdnsCode = EdnsCode(15);
301
302impl fmt::Display for EdnsCode {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        match self {
305            &EDNS_NSID => write!(f, "NSID"),
306            &EDNS_CLIENT_SUBNET => write!(f, "CLIENT_SUBNET"),
307            &EDNS_COOKIE => write!(f, "COOKIE"),
308            &EDNS_EDE => write!(f, "EXTENDED_DNS_ERROR"),
309            EdnsCode(c) => write!(f, "EDNS#{}", c),
310        }
311    }
312}
313
314impl fmt::Debug for EdnsCode {
315    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
316        write!(f, "EdnsCode({})", self)
317    }
318}
319
320#[derive(Debug, PartialEq, Eq, Copy, Clone)]
321pub struct EdeCode(u16);
322
323pub const EDE_OTHER: EdeCode = EdeCode(0);
324pub const EDE_UNSUPPORTED_DNSKEY_ALGO: EdeCode = EdeCode(1);
325pub const EDE_UNSUPPORTED_DS_DIGEST: EdeCode = EdeCode(2);
326pub const EDE_STALE_ANSWER: EdeCode = EdeCode(3);
327pub const EDE_FORGED_ANSWER: EdeCode = EdeCode(4);
328pub const EDE_DNSSEC_INDETERMINATE: EdeCode = EdeCode(5);
329pub const EDE_DNSSEC_BOGUS: EdeCode = EdeCode(6);
330pub const EDE_SIGNATURE_EXPIRED: EdeCode = EdeCode(7);
331pub const EDE_SIGNATURE_NOT_YET_VALID: EdeCode = EdeCode(8);
332pub const EDE_DNSKEY_MISSING: EdeCode = EdeCode(9);
333pub const EDE_RRSIG_MISSING: EdeCode = EdeCode(10);
334pub const EDE_NO_ZONE_KEY_BIT_SET: EdeCode = EdeCode(11);
335pub const EDE_NSEC_MISSING: EdeCode = EdeCode(12);
336pub const EDE_CACHED_ERROR: EdeCode = EdeCode(13);
337pub const EDE_NOT_READY: EdeCode = EdeCode(14);
338pub const EDE_BLOCKED: EdeCode = EdeCode(15);
339pub const EDE_CENSORED: EdeCode = EdeCode(16);
340pub const EDE_FILTERED: EdeCode = EdeCode(17);
341pub const EDE_PROHIBITED: EdeCode = EdeCode(18);
342pub const EDE_STALE_NXDOMAIN: EdeCode = EdeCode(19);
343pub const EDE_NOT_AUTHORITATIVE: EdeCode = EdeCode(20);
344pub const EDE_NOT_SUPPORTED: EdeCode = EdeCode(21);
345pub const EDE_NO_REACHABLE_AUTHORITY: EdeCode = EdeCode(22);
346pub const EDE_NETWORK_ERROR: EdeCode = EdeCode(23);
347pub const EDE_INVALID_DATA: EdeCode = EdeCode(24);
348
349impl fmt::Display for EdeCode {
350    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351        match self {
352            &EDE_OTHER => write!(f, "OTHER"),
353            &EDE_UNSUPPORTED_DNSKEY_ALGO => write!(f, "UNSUPPORTED_DNSKEY_ALGO"),
354            &EDE_UNSUPPORTED_DS_DIGEST => write!(f, "UNSUPPORTED_DS_DIGEST"),
355            &EDE_STALE_ANSWER => write!(f, "STALE_ANSWER"),
356            &EDE_FORGED_ANSWER => write!(f, "FORGED_ANSWER"),
357            &EDE_DNSSEC_INDETERMINATE => write!(f, "DNSSEC_INDETERMINATE"),
358            &EDE_DNSSEC_BOGUS => write!(f, "DNSSEC_BOGUS"),
359            &EDE_SIGNATURE_EXPIRED => write!(f, "SIGNATURE_EXPIRED"),
360            &EDE_SIGNATURE_NOT_YET_VALID => write!(f, "SIGNATURE_NOT_YET_VALID"),
361            &EDE_DNSKEY_MISSING => write!(f, "DNSKEY_MISSING"),
362            &EDE_RRSIG_MISSING => write!(f, "RRSIG_MISSING"),
363            &EDE_NO_ZONE_KEY_BIT_SET => write!(f, "NO_ZONE_KEY_BIT_SET"),
364            &EDE_NSEC_MISSING => write!(f, "NSEC_MISSING"),
365            &EDE_CACHED_ERROR => write!(f, "CACHED_ERROR"),
366            &EDE_NOT_READY => write!(f, "NOT_READY"),
367            &EDE_BLOCKED => write!(f, "BLOCKED"),
368            &EDE_CENSORED => write!(f, "CENSORED"),
369            &EDE_FILTERED => write!(f, "FILTERED"),
370            &EDE_PROHIBITED => write!(f, "PROHIBITED"),
371            &EDE_STALE_NXDOMAIN => write!(f, "STALE_NXDOMAIN"),
372            &EDE_NOT_AUTHORITATIVE => write!(f, "NOT_AUTHORITATIVE"),
373            &EDE_NOT_SUPPORTED => write!(f, "NOT_SUPPOTED"),
374            &EDE_NO_REACHABLE_AUTHORITY => write!(f, "NO_REACHABLE_AUTHORITY"),
375            &EDE_NETWORK_ERROR => write!(f, "NETWORK_ERROR"),
376            &EDE_INVALID_DATA => write!(f, "INVALID_DATA"),
377            EdeCode(c) => write!(f, "EdeCode({})", c),
378        }
379    }
380}
381
382#[derive(Clone, Eq, PartialEq)]
383#[cfg_attr(fuzzing, derive(Arbitrary))]
384pub struct EdnsOption {
385    pub code: EdnsCode,
386    pub data: Vec<u8>,
387}
388
389impl fmt::Debug for EdnsOption {
390    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
391        match self.code {
392            EDNS_EDE => write!(
393                f,
394                "EdnsOption({}: {})",
395                self.code,
396                String::from_utf8_lossy(&self.data[..])
397            ),
398            EDNS_COOKIE => write!(
399                f,
400                "EdnsOption({}: {})",
401                self.code,
402                self.data[..]
403                    .iter()
404                    .map(|b| format!("{:02x}", b))
405                    .collect::<Vec<_>>()
406                    .join("")
407            ),
408            ref code => write!(f, "EdnsOption({}: {:?})", code, self.data),
409        }
410    }
411}
412
413#[derive(Debug, Clone, Eq, PartialEq, Default)]
414#[cfg_attr(fuzzing, derive(Arbitrary))]
415pub struct EdnsData(Vec<EdnsOption>);
416
417#[derive(Debug, Clone, Eq, PartialEq)]
418#[cfg_attr(fuzzing, derive(Arbitrary))]
419pub struct SoaData {
420    pub mname: Domain,
421    pub rname: Domain,
422    pub serial: u32,
423    pub refresh: u32,
424    pub retry: u32,
425    pub expire: u32,
426    pub minimum: u32,
427}
428
429#[derive(Debug, Clone, Eq, PartialEq)]
430#[cfg_attr(fuzzing, derive(Arbitrary))]
431pub struct PrefDomainData {
432    pub pref: u16,
433    pub domain: Domain,
434}
435
436#[derive(Debug, Clone, Eq, PartialEq)]
437#[cfg_attr(fuzzing, derive(Arbitrary))]
438pub struct AFSDBData {
439    pub subtype: u16,
440    pub hostname: Domain,
441}
442
443#[derive(Debug, Clone, Eq, PartialEq)]
444#[cfg_attr(fuzzing, derive(Arbitrary))]
445pub struct RPData {
446    pub mbox: Domain,
447    pub txt: Domain,
448}
449
450#[derive(Debug, Clone, Eq, PartialEq)]
451pub struct NAPTRData {
452    pub order: u16,
453    pub preference: u16,
454    pub flags: Vec<u8>,
455    pub services: Vec<u8>,
456    pub regexp: Vec<u8>,
457    pub replacement: Domain,
458}
459
460#[cfg(fuzzing)]
461impl<'a> Arbitrary<'a> for NAPTRData {
462    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
463        let order = <_>::arbitrary(u)?;
464        let preference = <_>::arbitrary(u)?;
465        let mut flags: Vec<u8> = <_>::arbitrary(u)?;
466        let mut services: Vec<u8> = <_>::arbitrary(u)?;
467        let mut regexp: Vec<u8> = <_>::arbitrary(u)?;
468        let replacement = <_>::arbitrary(u)?;
469        flags.truncate(255);
470        services.truncate(255);
471        regexp.truncate(255);
472        Ok(NAPTRData {
473            order,
474            preference,
475            flags,
476            services,
477            regexp,
478            replacement,
479        })
480    }
481}
482
483#[derive(Debug, Clone, Eq, PartialEq)]
484#[cfg_attr(fuzzing, derive(Arbitrary))]
485pub enum RData {
486    CName(Domain),
487    Mx(PrefDomainData),
488    Ns(Domain),
489    Ptr(Domain),
490    Soa(SoaData),
491    Opt(EdnsData),
492    AfsDb(AFSDBData),
493    Rp(RPData),
494    Rt(PrefDomainData),
495    NaPtr(NAPTRData),
496    Other(Vec<u8>),
497}
498
499impl std::fmt::Display for RData {
500    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501        use RData::*;
502        match self {
503            CName(d) | Ns(d) | Ptr(d) => write!(f, "\"{}\"", d),
504            Mx(pd) | Rt(pd) => write!(f, "{} {}", pd.pref, pd.domain),
505            AfsDb(afs) => write!(f, "{} {}", afs.subtype, afs.hostname),
506            Rp(rp) => write!(f, "{} {}", rp.mbox, rp.txt),
507            NaPtr(na) => write!(
508                f,
509                "{} {} {:?} {:?} {:?} \"{}\"",
510                na.order, na.preference, na.flags, na.services, na.regexp, na.replacement
511            ),
512            Soa(v) => write!(
513                f,
514                "{:?} {:?} {} {} {} {} {}",
515                v.mname, v.rname, v.serial, v.refresh, v.retry, v.expire, v.minimum
516            ),
517            Opt(v) => write!(f, "{:?}", v),
518            Other(v) => write!(f, "\\#{} {:?}", v.len(), v),
519        }
520    }
521}
522
523#[derive(Clone, Eq, PartialEq)]
524pub struct RR {
525    pub domain: Domain,
526    pub class: Class,
527    pub rrtype: Type,
528    pub ttl: u32,
529    pub rdata: RData,
530}
531
532impl std::fmt::Display for RR {
533    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
534        write!(
535            f,
536            "\"{}\" {} {:?} {:?} {}",
537            self.domain, self.ttl, self.class, self.rrtype, self.rdata
538        )
539    }
540}
541
542impl fmt::Debug for RR {
543    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
544        write!(f, "RR({})", self)
545    }
546}
547
548#[cfg(fuzzing)]
549impl<'a> Arbitrary<'a> for RR {
550    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
551        let domain = <_>::arbitrary(u)?;
552        let class = <_>::arbitrary(u)?;
553        let ttl = <_>::arbitrary(u)?;
554        let rdata = <_>::arbitrary(u)?;
555        let rrtype = match &rdata {
556            RData::Ns(_) => RR_NS,
557            RData::CName(_) => RR_CNAME,
558            RData::Soa(_) => RR_SOA,
559            RData::Ptr(_) => RR_PTR,
560            RData::Mx(_) => RR_MX,
561            RData::Rp(_) => RR_RP,
562            RData::AfsDb(_) => RR_AFSDB,
563            RData::Rt(_) => RR_RT,
564            RData::NaPtr(_) => RR_NAPTR,
565            RData::Opt(_) => RR_OPT,
566            RData::Other(_) => loop {
567                /* Don't create RR_SOA or RR_OPT */
568                let rrtype = <_>::arbitrary(u)?;
569                if rrtype != RR_NS
570                    && rrtype != RR_CNAME
571                    && rrtype != RR_SOA
572                    && rrtype != RR_PTR
573                    && rrtype != RR_MX
574                    && rrtype != RR_RP
575                    && rrtype != RR_AFSDB
576                    && rrtype != RR_RT
577                    && rrtype != RR_NAPTR
578                    && rrtype != RR_OPT
579                {
580                    break rrtype;
581                }
582            },
583        };
584        return Ok(Self {
585            domain,
586            class,
587            rrtype,
588            ttl,
589            rdata,
590        });
591    }
592}
593
594#[derive(Ord, Eq, PartialOrd, PartialEq, Clone, Copy)]
595#[cfg_attr(fuzzing, derive(Arbitrary))]
596pub struct Opcode(pub u8);
597
598pub const OPCODE_QUERY: Opcode = Opcode(0);
599pub const OPCODE_IQUERY: Opcode = Opcode(1);
600pub const OPCODE_STATUS: Opcode = Opcode(2);
601pub const OPCODE_NOTIFY: Opcode = Opcode(4);
602pub const OPCODE_UPDATE: Opcode = Opcode(5);
603
604impl std::fmt::Display for Opcode {
605    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
606        match self {
607            &OPCODE_QUERY => write!(f, "QUERY"),
608            &OPCODE_IQUERY => write!(f, "IQUERY"),
609            &OPCODE_STATUS => write!(f, "STATUS"),
610            &OPCODE_NOTIFY => write!(f, "NOTIFY"),
611            &OPCODE_UPDATE => write!(f, "UPDATE"),
612            Opcode(x) => write!(f, "#{}", x),
613        }
614    }
615}
616
617impl fmt::Debug for Opcode {
618    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
619        write!(f, "Opcode({})", self)
620    }
621}
622
623#[derive(Debug, Clone, Eq, PartialEq)]
624pub struct DNSPkt {
625    pub qid: u16,
626    pub rd: bool,
627    pub tc: bool,
628    pub aa: bool,
629    pub qr: bool,
630    pub opcode: Opcode,
631
632    pub cd: bool,
633    pub ad: bool,
634    pub ra: bool,
635    pub rcode: RCode,
636    pub bufsize: u16,
637    pub edns_ver: Option<u8>,
638    pub edns_do: bool,
639
640    pub question: Question,
641
642    pub answer: Vec<RR>,
643    pub nameserver: Vec<RR>,
644    pub additional: Vec<RR>,
645
646    pub edns: Option<EdnsData>,
647}
648
649#[cfg(fuzzing)]
650impl<'a> Arbitrary<'a> for DNSPkt {
651    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
652        let qid = <_>::arbitrary(u)?;
653        let rd = <_>::arbitrary(u)?;
654        let tc = <_>::arbitrary(u)?;
655        let aa = <_>::arbitrary(u)?;
656        let qr = <_>::arbitrary(u)?;
657        let opcode: u8 = <_>::arbitrary(u)?;
658        let cd = <_>::arbitrary(u)?;
659        let ad = <_>::arbitrary(u)?;
660        let ra = <_>::arbitrary(u)?;
661        let rcode = loop {
662            let rcode = RCode::arbitrary(u)?;
663            if rcode.0 <= 0b1111_1111_1111u16 {
664                break rcode;
665            }
666        };
667        let bufsize = <_>::arbitrary(u)?;
668        let edns_ver = <_>::arbitrary(u)?;
669        let edns_do = <_>::arbitrary(u)?;
670        let question = <_>::arbitrary(u)?;
671        let answer = <_>::arbitrary(u)?;
672        let additional = <_>::arbitrary(u)?;
673        let nameserver = <_>::arbitrary(u)?;
674        let edns = <_>::arbitrary(u)?;
675
676        return Ok(Self {
677            qid,
678            rd,
679            tc,
680            aa,
681            qr,
682            opcode: Opcode(opcode % 31),
683            cd,
684            ad,
685            ra,
686            rcode,
687            bufsize,
688            edns_ver,
689            edns_do,
690            question,
691            answer,
692            additional,
693            nameserver,
694            edns,
695        });
696    }
697}
698
699#[derive(Clone)]
700struct DomainTree<T: Default> {
701    label: Label,
702    data: T,
703    children: std::collections::LinkedList<Self>,
704}
705
706#[cfg(any(test, fuzzing))]
707impl<T: fmt::Debug + Default> fmt::Debug for DomainTree<T> {
708    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
709        write!(
710            f,
711            "DomainTree(label: {}, data: {:?}, children: {:#?})",
712            self.label, self.data, self.children
713        )
714    }
715}
716
717impl<T: Default> DomainTree<T> {
718    fn new() -> Self {
719        DomainTree {
720            label: Label("root".into()),
721            data: Default::default(),
722            children: Default::default(),
723        }
724    }
725}
726
727type DomainOffsets = DomainTree<u16>;
728
729fn push_u16(v: &mut Vec<u8>, d: u16) {
730    v.push((d >> 8) as u8);
731    v.push((d & 0xFF) as u8);
732}
733
734fn push_u32(v: &mut Vec<u8>, d: u32) {
735    v.push(((d >> 24) & 0xFF) as u8);
736    v.push(((d >> 16) & 0xFF) as u8);
737    v.push(((d >> 8) & 0xFF) as u8);
738    v.push((d & 0xFF) as u8);
739}
740
741fn push_label(v: &mut Vec<u8>, l: &Label) {
742    assert!(!l.0.is_empty());
743    assert!(l.0.len() < 64);
744    v.push(l.0.len() as u8);
745    v.extend_from_slice(l.0.as_slice())
746}
747
748/* This does label compression.
749 * node is Some if some node exists in the suffix tree for the suffix.
750 * node is None if no node exists (and a label will need to be output).
751 * this returns Some if this created a new node that needs to be appended to the tree (and also if
752 * it output a label)
753 * this returns None if there was an existing node, *and* it output the compression for the entire
754 * suffix (so no more suffixes are needed).
755 */
756fn push_prefix(
757    v: &mut Vec<u8>,
758    l: &[Label],
759    node: &mut Option<&mut DomainOffsets>,
760    base_offset: usize,
761) -> Option<DomainOffsets> {
762    assert!(!l.is_empty());
763    let label = &l[l.len() - 1];
764    let prefix = &l[..l.len() - 1];
765    let mut child = None;
766    if let Some(node) = node {
767        for it in &mut node.children {
768            if it.label == *label {
769                child = Some(&mut *it);
770            }
771        }
772    }
773    if !prefix.is_empty() {
774        let ret = push_prefix(v, prefix, &mut child, base_offset);
775        match (ret, child) {
776            (None, None) => {
777                /* They output a suffix, but we aren't part of that suffix?! */
778                unreachable!()
779            }
780            (None, Some(_)) => {
781                /* They output a suffix, we are part of the suffix, nothing to do. */
782                None
783            }
784            (Some(r), None) => {
785                /* They have output a label, we need to output a label. */
786                let offset = v.len() + base_offset;
787                push_label(v, label);
788                let mut children = std::collections::LinkedList::new();
789                children.push_back(r);
790                Some(DomainTree {
791                    label: label.clone(),
792                    data: offset as u16,
793                    children,
794                })
795            }
796            (Some(r), Some(n)) => {
797                /* They have output a label, we need to output a suffix */
798                n.children.push_back(r);
799                assert!((n.data >> 8) < 64);
800                assert_ne!(n.data, 0);
801                v.push(0b1100_0000u8 + (n.data >> 8) as u8);
802                v.push((n.data & 0xff) as u8);
803                None
804            }
805        }
806    } else {
807        match &child {
808            None => {
809                /* Not found - we need to output our label */
810                let offset = v.len() + base_offset;
811                push_label(v, label);
812                Some(DomainTree {
813                    label: label.clone(),
814                    data: offset as u16,
815                    children: std::collections::LinkedList::new(),
816                })
817            }
818            Some(n) => {
819                /* Found - we can output the entire domain as one compressed label. */
820                v.push(0b1100_0000u8 + (n.data >> 8) as u8);
821                v.push((n.data & 0xff) as u8);
822                None
823            }
824        }
825    }
826}
827
828fn push_compressed_domain(
829    v: &mut Vec<u8>,
830    d: &Domain,
831    offsets: &mut DomainOffsets,
832    base_offset: usize,
833) {
834    if d.0.is_empty() {
835        v.push(0u8);
836    } else {
837        match push_prefix(v, &d.0, &mut Some(offsets), base_offset) {
838            None => { /* Suffix compression already output */ }
839            Some(n) => {
840                offsets.children.push_back(n);
841                v.push(0u8);
842            }
843        }
844    }
845}
846
847fn push_str(v: &mut Vec<u8>, s: &[u8]) {
848    assert!(s.len() < 256);
849    v.push(s.len() as u8);
850    v.extend(s);
851}
852
853fn make_edns_opt(v: &mut Vec<u8>, t: &EdnsOption) {
854    push_u16(v, t.code.0);
855    push_u16(v, t.data.len() as u16);
856    v.extend_from_slice(t.data.as_slice());
857}
858
859impl EdnsData {
860    pub const fn new() -> Self {
861        Self(vec![])
862    }
863    fn push_opt(&self, v: &mut Vec<u8>) {
864        self.0.iter().for_each(|o| make_edns_opt(v, o));
865    }
866
867    fn get_opt(&self, opt: &EdnsCode) -> Option<&EdnsOption> {
868        self.0.iter().find(|o| o.code == *opt)
869    }
870
871    pub fn get_nsid(&self) -> Option<&[u8]> {
872        self.get_opt(&EDNS_NSID).map(|opt| &opt.data[..])
873    }
874
875    pub fn set_nsid(&mut self, nsid: &[u8]) {
876        self.set_opt(EdnsOption {
877            code: EDNS_NSID,
878            data: nsid.to_vec(),
879        });
880    }
881
882    pub fn get_cookie(&self) -> Option<(&[u8], Option<&[u8]>)> {
883        self.get_opt(&EDNS_COOKIE)
884            .map(|opt| (&opt.data[..8], opt.data.get(8..)))
885    }
886
887    pub fn set_cookie(&mut self, client: &[u8], server: &[u8]) {
888        assert!(client.len() == 8);
889        assert!(server.len() >= 8 && server.len() <= 32);
890        let mut data = Vec::with_capacity(client.len() + server.len());
891        data.extend(client);
892        data.extend(server);
893        self.set_opt(EdnsOption {
894            code: EDNS_COOKIE,
895            data,
896        })
897    }
898
899    pub fn get_extended_dns_error(&self) -> Option<(EdeCode, String)> {
900        self.get_opt(&EDNS_EDE).map(|opt| {
901            (
902                EdeCode(u16::from_be_bytes([opt.data[0], opt.data[1]])),
903                String::from_utf8_lossy(&opt.data[2..]).into_owned(),
904            )
905        })
906    }
907
908    pub fn set_opt(&mut self, opt: EdnsOption) {
909        self.0.push(opt);
910    }
911
912    pub fn set_extended_dns_error(&mut self, code: EdeCode, msg: &str) {
913        let mut data = vec![];
914        data.extend(code.0.to_be_bytes().iter());
915        data.extend(msg.bytes());
916        self.set_opt(EdnsOption {
917            code: EDNS_EDE,
918            data,
919        });
920    }
921}
922
923fn push_rr(v: &mut Vec<u8>, rr: &RR, offsets: &mut DomainOffsets) {
924    push_compressed_domain(v, &rr.domain, offsets, 0);
925    push_u16(v, rr.rrtype.0);
926    push_u16(v, rr.class.0);
927    push_u32(v, rr.ttl);
928    match &rr.rdata {
929        RData::CName(d) | RData::Ptr(d) | RData::Ns(d) => {
930            let mut vs = vec![];
931            push_compressed_domain(&mut vs, d, offsets, v.len() + 2);
932            push_u16(v, vs.len() as u16);
933            v.extend_from_slice(vs.as_slice());
934        }
935        RData::Mx(pd) | RData::Rt(pd) => {
936            let mut vs = vec![];
937            push_u16(&mut vs, pd.pref);
938            push_compressed_domain(&mut vs, &pd.domain, offsets, v.len() + 2);
939            push_u16(v, vs.len() as u16);
940            v.extend_from_slice(vs.as_slice());
941        }
942        RData::NaPtr(na) => {
943            let mut vs = vec![];
944            push_u16(&mut vs, na.order);
945            push_u16(&mut vs, na.preference);
946            push_str(&mut vs, &na.flags);
947            push_str(&mut vs, &na.services);
948            push_str(&mut vs, &na.regexp);
949            push_compressed_domain(&mut vs, &na.replacement, offsets, v.len() + 2);
950            push_u16(v, vs.len() as u16);
951            v.extend_from_slice(vs.as_slice());
952        }
953        RData::Rp(rp) => {
954            let mut vs = vec![];
955            push_compressed_domain(&mut vs, &rp.mbox, offsets, v.len() + 2);
956            push_compressed_domain(&mut vs, &rp.txt, offsets, v.len() + 2);
957            push_u16(v, vs.len() as u16);
958            v.extend_from_slice(vs.as_slice());
959        }
960        RData::Soa(s) => {
961            assert!(rr.rrtype == RR_SOA);
962            let mut vs = vec![];
963            push_compressed_domain(&mut vs, &s.mname, offsets, v.len() + 2);
964            push_compressed_domain(&mut vs, &s.rname, offsets, v.len() + 2);
965            push_u32(&mut vs, s.serial);
966            push_u32(&mut vs, s.refresh);
967            push_u32(&mut vs, s.retry);
968            push_u32(&mut vs, s.expire);
969            push_u32(&mut vs, s.minimum);
970
971            push_u16(v, vs.len() as u16);
972            v.extend_from_slice(vs.as_slice());
973        }
974        RData::AfsDb(afs) => {
975            let mut vs = vec![];
976            push_u16(&mut vs, afs.subtype);
977            push_compressed_domain(&mut vs, &afs.hostname, offsets, v.len() + 2);
978            push_u16(v, vs.len() as u16);
979            v.extend_from_slice(vs.as_slice());
980        }
981        RData::Opt(o) => {
982            assert!(rr.rrtype == RR_OPT);
983            let mut vo = vec![];
984            o.push_opt(&mut vo);
985
986            push_u16(v, vo.len() as u16);
987            v.extend_from_slice(vo.as_slice());
988        }
989        RData::Other(x) => {
990            use std::convert::TryFrom as _;
991            assert!(rr.rrtype != RR_OPT && rr.rrtype != RR_SOA);
992            push_u16(v, u16::try_from(x.len()).unwrap());
993            v.extend_from_slice(x.as_slice());
994        }
995    }
996}
997
998impl DNSPkt {
999    pub fn status(&self) -> String {
1000        match self
1001            .edns
1002            .as_ref()
1003            .and_then(|e| e.get_extended_dns_error())
1004            .map(|e| e.0)
1005        {
1006            Some(x) => format!("{} ({})", self.rcode, x),
1007            None => format!("{}", self.rcode),
1008        }
1009    }
1010    pub fn serialise(&self) -> Vec<u8> {
1011        self.serialise_with_size(65536)
1012    }
1013    pub fn serialise_with_size(&self, size: usize) -> Vec<u8> {
1014        assert!(size >= 512);
1015        let mut ret: Vec<u8> = Vec::new();
1016        let mut offsets = DomainOffsets::new();
1017        assert!(self.rcode.0 <= 0b1111_1111_1111);
1018        let flag1: u8 = u8::from(self.rd)
1019            | (if self.tc { 0b0000_0010 } else { 0b0 })
1020            | (if self.aa { 0b0000_0100 } else { 0b0 })
1021            | (if self.qr { 0b1000_0000 } else { 0b0 })
1022            | (self.opcode.0 << 3);
1023        let flag2: u8 = (if self.cd { 0b0010_0000 } else { 0b0 })
1024            |(if self.ad { 0b0100_0000 } else { 0b0 })
1025            |(if self.ra { 0b1000_0000 } else { 0b0 })
1026            //             0b0001_0000
1027            |((self.rcode.0 & 0b0000_1111) as u8);
1028        let mut additional = self.additional.clone();
1029
1030        if self.edns.is_some() {
1031            let edns = self.edns.clone().unwrap_or_default();
1032
1033            additional.push(RR {
1034                domain: Domain::from(vec![]),
1035                class: Class(self.bufsize),
1036                rrtype: RR_OPT,
1037                ttl: (((self.rcode.0 >> 4) as u32) << 24)
1038                    | ((self.edns_ver.unwrap_or(0) as u32) << 16)
1039                    | (if self.edns_do {
1040                        0b0000_0000_0000_0000_1000_0000_0000_0000
1041                    } else {
1042                        0b0
1043                    }),
1044                rdata: RData::Opt(edns),
1045            });
1046        }
1047
1048        push_u16(&mut ret, self.qid);
1049        ret.push(flag1);
1050        ret.push(flag2);
1051        push_u16(&mut ret, 1); // qcount
1052        push_u16(&mut ret, self.answer.len() as u16);
1053        push_u16(&mut ret, self.nameserver.len() as u16);
1054        push_u16(&mut ret, additional.len() as u16);
1055        push_compressed_domain(&mut ret, &self.question.qdomain, &mut offsets, 0);
1056        push_u16(&mut ret, self.question.qtype.0);
1057        push_u16(&mut ret, self.question.qclass.0);
1058
1059        let mut trunc = false;
1060        let mut ancount: u16 = 0;
1061        let mut nscount: u16 = 0;
1062        let mut adcount: u16 = 0;
1063
1064        for rr in &self.answer {
1065            let offset = ret.len();
1066            push_rr(&mut ret, rr, &mut offsets);
1067            if ret.len() > size {
1068                ret.truncate(offset);
1069                trunc = true;
1070                break;
1071            } else {
1072                ancount += 1;
1073            }
1074        }
1075
1076        if !trunc {
1077            for rr in &self.nameserver {
1078                let offset = ret.len();
1079                push_rr(&mut ret, rr, &mut offsets);
1080                if ret.len() > size {
1081                    ret.truncate(offset);
1082                    trunc = true;
1083                    break;
1084                } else {
1085                    nscount += 1;
1086                }
1087            }
1088        }
1089
1090        if !trunc {
1091            for rr in &additional {
1092                let offset = ret.len();
1093                push_rr(&mut ret, rr, &mut offsets);
1094                if ret.len() > size {
1095                    ret.truncate(offset);
1096                    trunc = true;
1097                    break;
1098                } else {
1099                    adcount += 1;
1100                }
1101            }
1102        }
1103
1104        if trunc {
1105            // Update the header with the fact we truncated this.
1106            ret[2] |= 0b0000_0010;
1107            ret.splice(6..7, ancount.to_be_bytes().iter().copied());
1108            ret.splice(8..9, nscount.to_be_bytes().iter().copied());
1109            ret.splice(10..11, adcount.to_be_bytes().iter().copied());
1110        }
1111
1112        ret
1113    }
1114
1115    pub fn get_expiry(&self) -> std::time::Duration {
1116        self.answer
1117            .iter()
1118            .chain(self.nameserver.iter())
1119            .chain(self.additional.iter())
1120            .map(|rr| std::time::Duration::from_secs(rr.ttl as u64))
1121            .min()
1122            .unwrap_or_else(|| std::time::Duration::from_secs(0))
1123    }
1124
1125    #[must_use]
1126    pub fn clone_with_ttl_decrement(&self, decrement: u32) -> DNSPkt {
1127        DNSPkt {
1128            question: self.question.clone(),
1129            additional: self
1130                .additional
1131                .iter()
1132                .map(|x| RR {
1133                    ttl: x.ttl - decrement,
1134                    ..x.clone()
1135                })
1136                .collect(),
1137            nameserver: self
1138                .nameserver
1139                .iter()
1140                .map(|x| RR {
1141                    ttl: x.ttl - decrement,
1142                    ..x.clone()
1143                })
1144                .collect(),
1145            answer: self
1146                .answer
1147                .iter()
1148                .map(|x| RR {
1149                    ttl: x.ttl - decrement,
1150                    ..x.clone()
1151                })
1152                .collect(),
1153            edns: self.edns.clone(),
1154            ..*self
1155        }
1156    }
1157}
1158
1159#[test]
1160fn test_compressed_domain() {
1161    let mut v = vec![];
1162    let domain = Domain(vec![]);
1163    let mut offsets = DomainOffsets::new();
1164    push_compressed_domain(&mut v, &domain, &mut offsets, 0);
1165    push_compressed_domain(&mut v, &"local".parse().unwrap(), &mut offsets, 0);
1166    push_compressed_domain(&mut v, &"c.d.example.com".parse().unwrap(), &mut offsets, 0);
1167    push_compressed_domain(
1168        &mut v,
1169        &"a.b.c.d.example.com".parse().unwrap(),
1170        &mut offsets,
1171        0,
1172    );
1173    assert_eq!(
1174        v,
1175        [
1176            0, /* empty domain */
1177            5, 108, 111, 99, 97, 108, 0, /* local */
1178            1, 99, 1, 100, 7, 101, 120, 97, 109, 112, 108, 101, 3, 99, 111, 109,
1179            0, /* c.d.example.com */
1180            1, 97, 1, 98, 192, 8 /* a.b.<offset 8> */
1181        ]
1182    );
1183}
1184
1185#[test]
1186fn test_compression_roundtrip() {
1187    let mut v = vec![];
1188    let mut offsets = DomainOffsets::new();
1189    push_compressed_domain(
1190        &mut v,
1191        &"test.example.com".parse().unwrap(),
1192        &mut offsets,
1193        0,
1194    );
1195    push_compressed_domain(
1196        &mut v,
1197        &"test2.example.com".parse().unwrap(),
1198        &mut offsets,
1199        0,
1200    );
1201    push_compressed_domain(
1202        &mut v,
1203        &"test.example.com".parse().unwrap(),
1204        &mut offsets,
1205        0,
1206    );
1207    let mut p = super::parse::PktParser::new(&v);
1208    assert_eq!(p.get_domain().unwrap(), "test.example.com".parse().unwrap());
1209    assert_eq!(
1210        p.get_domain().unwrap(),
1211        "test2.example.com".parse().unwrap()
1212    );
1213    assert_eq!(p.get_domain().unwrap(), "test.example.com".parse().unwrap());
1214}
1215
1216#[test]
1217fn test_rr_roundtrip() {
1218    let mut v = vec![];
1219    let mut offsets = DomainOffsets::new();
1220    let orig_cname = RR {
1221        domain: "test.example.com".parse().unwrap(),
1222        class: CLASS_IN,
1223        rrtype: RR_CNAME,
1224        ttl: 300,
1225        rdata: RData::CName("test.example.com".parse().unwrap()),
1226    };
1227    let orig_naptr = RR {
1228        domain: "test.example.com".parse().unwrap(),
1229        class: CLASS_IN,
1230        rrtype: RR_NAPTR,
1231        ttl: 300,
1232        rdata: RData::NaPtr(NAPTRData {
1233            order: 10,
1234            preference: 20,
1235            flags: "FLAG".into(),
1236            services: "SERVICE SERVICE".into(),
1237            regexp: "REGEXP".into(),
1238            replacement: "test.example.com".parse().unwrap(),
1239        }),
1240    };
1241    push_rr(&mut v, &orig_cname, &mut offsets);
1242    push_rr(&mut v, &orig_naptr, &mut offsets);
1243    let mut p = super::parse::PktParser::new(&v);
1244    assert_eq!(orig_cname, p.get_rr().unwrap());
1245    assert_eq!(orig_naptr, p.get_rr().unwrap());
1246}
1247
1248#[test]
1249fn test_pkt_roundtrip() {
1250    let mut orig_edns = EdnsData::new();
1251    orig_edns.set_extended_dns_error(EDE_OTHER, "Testing");
1252    let orig_pkt = DNSPkt {
1253        qid: 140,
1254        rd: false,
1255        tc: false,
1256        aa: false,
1257        qr: true,
1258        opcode: Opcode(15),
1259        cd: false,
1260        ad: false,
1261        ra: false,
1262        rcode: NOERROR,
1263        bufsize: 512,
1264        edns_ver: Some(0),
1265        edns_do: false,
1266        question: Question {
1267            qdomain: Domain::from(vec![]),
1268            qclass: Class(2570),
1269            qtype: Type(768),
1270        },
1271        answer: vec![
1272            RR {
1273                domain: Domain::from(vec![]),
1274                ttl: 16843009,
1275                class: Class(257),
1276                rrtype: RR_NAPTR,
1277                rdata: RData::NaPtr(NAPTRData {
1278                    order: 47288,
1279                    preference: 11960,
1280                    flags: "flags".into(),
1281                    services: "".into(),
1282                    regexp: "\u{1}".into(),
1283                    replacement: Domain::from(vec![]),
1284                }),
1285            },
1286            RR {
1287                domain: Domain::from(vec![]),
1288                ttl: 1234,
1289                class: CLASS_IN,
1290                rrtype: RR_SOA,
1291                rdata: RData::Soa(SoaData {
1292                    mname: "dnsmaster.example.com".parse().unwrap(),
1293                    rname: "ns1.example.com".parse().unwrap(),
1294                    serial: 1,
1295                    refresh: 3600,
1296                    retry: 300,
1297                    expire: 86400,
1298                    minimum: 600,
1299                }),
1300            },
1301            RR {
1302                domain: Domain::from(vec![]),
1303                ttl: 1234,
1304                class: CLASS_IN,
1305                rrtype: RR_MX,
1306                rdata: RData::Mx(PrefDomainData {
1307                    pref: 10,
1308                    domain: "mx.example.com".parse().unwrap(),
1309                }),
1310            },
1311        ],
1312        nameserver: vec![],
1313        additional: vec![],
1314        edns: Some(orig_edns),
1315    };
1316    let v = orig_pkt.serialise();
1317    let mut p = super::parse::PktParser::new(&v);
1318    assert_eq!(orig_pkt, p.get_dns().unwrap());
1319}
1320
1321#[test]
1322fn domain_from_str() {
1323    assert_eq!(
1324        "example.com".parse(),
1325        Ok(Domain(vec![
1326            Label(vec![b'e', b'x', b'a', b'm', b'p', b'l', b'e']),
1327            Label(vec![b'c', b'o', b'm'])
1328        ]))
1329    );
1330}