nex_packet/
dns.rs

1use alloc::string::String;
2use alloc::vec::Vec;
3use core::{fmt, str};
4use nex_macro::packet;
5use nex_macro_helper::packet::{Packet, PacketSize, PrimitiveValues};
6use nex_macro_helper::types::{u1, u16be, u32be, u4};
7use std::str::Utf8Error;
8
9/// Represents an DNS operation.
10/// These identifiers correspond to DNS resource record classes.
11/// <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2>
12#[allow(non_snake_case)]
13#[allow(non_upper_case_globals)]
14pub mod DnsClasses {
15    use super::DnsClass;
16
17    /// Internet
18    pub const IN: DnsClass = DnsClass(1);
19    /// CSNET (Unassigned)
20    pub const CS: DnsClass = DnsClass(2);
21    /// Chaos
22    pub const CH: DnsClass = DnsClass(3);
23    /// Hesiod
24    pub const HS: DnsClass = DnsClass(4);
25}
26
27/// Represents a DNS class.
28#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct DnsClass(pub u16);
30
31impl DnsClass {
32    pub fn new(value: u16) -> Self {
33        Self(value)
34    }
35}
36
37impl PrimitiveValues for DnsClass {
38    type T = (u16,);
39
40    fn to_primitive_values(&self) -> (u16,) {
41        (self.0,)
42    }
43}
44
45impl fmt::Display for DnsClass {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        write!(
48            f,
49            "{}",
50            match self {
51                &DnsClasses::IN => "IN", // 1
52                &DnsClasses::CS => "CS", // 2
53                &DnsClasses::CH => "CH", // 3
54                &DnsClasses::HS => "HS", // 4
55                _ => "unknown",
56            }
57        )
58    }
59}
60
61/// Represents an DNS types.
62/// These identifiers are used to specify the type of DNS query or response.
63/// <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4>
64#[allow(non_snake_case)]
65#[allow(non_upper_case_globals)]
66pub mod DnsTypes {
67    use super::DnsType;
68
69    pub const A: DnsType = DnsType(1);
70    pub const NS: DnsType = DnsType(2);
71    pub const MD: DnsType = DnsType(3);
72    pub const MF: DnsType = DnsType(4);
73    pub const CNAME: DnsType = DnsType(5);
74    pub const SOA: DnsType = DnsType(6);
75    pub const MB: DnsType = DnsType(7);
76    pub const MG: DnsType = DnsType(8);
77    pub const MR: DnsType = DnsType(9);
78    pub const NULL: DnsType = DnsType(10);
79    pub const WKS: DnsType = DnsType(11);
80    pub const PTR: DnsType = DnsType(12);
81    pub const HINFO: DnsType = DnsType(13);
82    pub const MINFO: DnsType = DnsType(14);
83    pub const MX: DnsType = DnsType(15);
84    pub const TXT: DnsType = DnsType(16);
85    pub const RP: DnsType = DnsType(17);
86    pub const AFSDB: DnsType = DnsType(18);
87    pub const X25: DnsType = DnsType(19);
88    pub const ISDN: DnsType = DnsType(20);
89    pub const RT: DnsType = DnsType(21);
90    pub const NSAP: DnsType = DnsType(22);
91    pub const NSAP_PTR: DnsType = DnsType(23);
92    pub const SIG: DnsType = DnsType(24);
93    pub const KEY: DnsType = DnsType(25);
94    pub const PX: DnsType = DnsType(26);
95    pub const GPOS: DnsType = DnsType(27);
96    pub const AAAA: DnsType = DnsType(28);
97    pub const LOC: DnsType = DnsType(29);
98    pub const NXT: DnsType = DnsType(30);
99    pub const EID: DnsType = DnsType(31);
100    pub const NIMLOC: DnsType = DnsType(32);
101    pub const SRV: DnsType = DnsType(33);
102    pub const ATMA: DnsType = DnsType(34);
103    pub const NAPTR: DnsType = DnsType(35);
104    pub const KX: DnsType = DnsType(36);
105    pub const CERT: DnsType = DnsType(37);
106    pub const A6: DnsType = DnsType(38);
107    pub const DNAME: DnsType = DnsType(39);
108    pub const SINK: DnsType = DnsType(40);
109    pub const OPT: DnsType = DnsType(41);
110    pub const APL: DnsType = DnsType(42);
111    pub const DS: DnsType = DnsType(43);
112    pub const SSHFP: DnsType = DnsType(44);
113    pub const IPSECKEY: DnsType = DnsType(45);
114    pub const RRSIG: DnsType = DnsType(46);
115    pub const NSEC: DnsType = DnsType(47);
116    pub const DNSKEY: DnsType = DnsType(48);
117    pub const DHCID: DnsType = DnsType(49);
118    pub const NSEC3: DnsType = DnsType(50);
119    pub const NSEC3PARAM: DnsType = DnsType(51);
120    pub const TLSA: DnsType = DnsType(52);
121    pub const SMIMEA: DnsType = DnsType(53);
122    pub const HIP: DnsType = DnsType(55);
123    pub const NINFO: DnsType = DnsType(56);
124    pub const RKEY: DnsType = DnsType(57);
125    pub const TALINK: DnsType = DnsType(58);
126    pub const CDS: DnsType = DnsType(59);
127    pub const CDNSKEY: DnsType = DnsType(60);
128    pub const OPENPGPKEY: DnsType = DnsType(61);
129    pub const CSYNC: DnsType = DnsType(62);
130    pub const ZONEMD: DnsType = DnsType(63);
131    pub const SVCB: DnsType = DnsType(64);
132    pub const HTTPS: DnsType = DnsType(65);
133    pub const SPF: DnsType = DnsType(99);
134    pub const UINFO: DnsType = DnsType(100);
135    pub const UID: DnsType = DnsType(101);
136    pub const GID: DnsType = DnsType(102);
137    pub const UNSPEC: DnsType = DnsType(103);
138    pub const NID: DnsType = DnsType(104);
139    pub const L32: DnsType = DnsType(105);
140    pub const L64: DnsType = DnsType(106);
141    pub const LP: DnsType = DnsType(107);
142    pub const EUI48: DnsType = DnsType(108);
143    pub const EUI64: DnsType = DnsType(109);
144    pub const TKEY: DnsType = DnsType(249);
145    pub const TSIG: DnsType = DnsType(250);
146    pub const IXFR: DnsType = DnsType(251);
147    pub const AXFR: DnsType = DnsType(252);
148    pub const MAILB: DnsType = DnsType(253);
149    pub const MAILA: DnsType = DnsType(254);
150    pub const ANY: DnsType = DnsType(255);
151    pub const URI: DnsType = DnsType(256);
152    pub const CAA: DnsType = DnsType(257);
153    pub const AVC: DnsType = DnsType(258);
154    pub const DOA: DnsType = DnsType(259);
155    pub const AMTRELAY: DnsType = DnsType(260);
156    pub const TA: DnsType = DnsType(32768);
157    pub const DLV: DnsType = DnsType(32769);
158}
159
160/// Represents a DNS type.
161#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
162pub struct DnsType(pub u16);
163
164impl DnsType {
165    pub fn new(value: u16) -> Self {
166        Self(value)
167    }
168}
169
170impl PrimitiveValues for DnsType {
171    type T = (u16,);
172
173    fn to_primitive_values(&self) -> (u16,) {
174        (self.0,)
175    }
176}
177
178impl fmt::Display for DnsType {
179    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180        write!(
181            f,
182            "{}",
183            match self {
184                &DnsTypes::A => "A",                   // 1
185                &DnsTypes::NS => "NS",                 // 2
186                &DnsTypes::MD => "MD",                 // 3
187                &DnsTypes::MF => "MF",                 // 4
188                &DnsTypes::CNAME => "CNAME",           // 5
189                &DnsTypes::SOA => "SOA",               // 6
190                &DnsTypes::MB => "MB",                 // 7
191                &DnsTypes::MG => "MG",                 // 8
192                &DnsTypes::MR => "MR",                 // 9
193                &DnsTypes::NULL => "NULL",             // 10
194                &DnsTypes::WKS => "WKS",               // 11
195                &DnsTypes::PTR => "PTR",               // 12
196                &DnsTypes::HINFO => "HINFO",           // 13
197                &DnsTypes::MINFO => "MINFO",           // 14
198                &DnsTypes::MX => "MX",                 // 15
199                &DnsTypes::TXT => "TXT",               // 16
200                &DnsTypes::RP => "RP",                 // 17
201                &DnsTypes::AFSDB => "AFSDB",           // 18
202                &DnsTypes::X25 => "X25",               // 19
203                &DnsTypes::ISDN => "ISDN",             // 20
204                &DnsTypes::RT => "RT",                 // 21
205                &DnsTypes::NSAP => "NSAP",             // 22
206                &DnsTypes::NSAP_PTR => "NSAP_PTR",     // 23
207                &DnsTypes::SIG => "SIG",               // 24
208                &DnsTypes::KEY => "KEY",               // 25
209                &DnsTypes::PX => "PX",                 // 26
210                &DnsTypes::GPOS => "GPOS",             // 27
211                &DnsTypes::AAAA => "AAAA",             // 28
212                &DnsTypes::LOC => "LOC",               // 29
213                &DnsTypes::NXT => "NXT",               // 30
214                &DnsTypes::EID => "EID",               // 31
215                &DnsTypes::NIMLOC => "NIMLOC",         // 32
216                &DnsTypes::SRV => "SRV",               // 33
217                &DnsTypes::ATMA => "ATMA",             // 34
218                &DnsTypes::NAPTR => "NAPTR",           // 35
219                &DnsTypes::KX => "KX",                 // 36
220                &DnsTypes::CERT => "CERT",             // 37
221                &DnsTypes::A6 => "A6",                 // 38
222                &DnsTypes::DNAME => "DNAME",           // 39
223                &DnsTypes::SINK => "SINK",             // 40
224                &DnsTypes::OPT => "OPT",               // 41
225                &DnsTypes::APL => "APL",               // 42
226                &DnsTypes::DS => "DS",                 // 43
227                &DnsTypes::SSHFP => "SSHFP",           // 44
228                &DnsTypes::IPSECKEY => "IPSECKEY",     // 45
229                &DnsTypes::RRSIG => "RRSIG",           // 46
230                &DnsTypes::NSEC => "NSEC",             // 47
231                &DnsTypes::DNSKEY => "DNSKEY",         // 48
232                &DnsTypes::DHCID => "DHCID",           // 49
233                &DnsTypes::NSEC3 => "NSEC3",           // 50
234                &DnsTypes::NSEC3PARAM => "NSEC3PARAM", // 51
235                &DnsTypes::TLSA => "TLSA",             // 52
236                &DnsTypes::SMIMEA => "SMIMEA",         // 53
237                &DnsTypes::HIP => "HIP",               // 55
238                &DnsTypes::NINFO => "NINFO",           // 56
239                &DnsTypes::RKEY => "RKEY",             // 57
240                &DnsTypes::TALINK => "TALINK",         // 58
241                &DnsTypes::CDS => "CDS",               // 59
242                &DnsTypes::CDNSKEY => "CDNSKEY",       // 60
243                &DnsTypes::OPENPGPKEY => "OPENPGPKEY", // 61
244                &DnsTypes::CSYNC => "CSYNC",           // 62
245                &DnsTypes::ZONEMD => "ZONEMD",         // 63
246                &DnsTypes::SVCB => "SVCB",             // 64
247                &DnsTypes::HTTPS => "HTTPS",           // 65
248                &DnsTypes::SPF => "SPF",               // 99
249                &DnsTypes::UINFO => "UINFO",           // 100
250                &DnsTypes::UID => "UID",               // 101
251                &DnsTypes::GID => "GID",               // 102
252                &DnsTypes::UNSPEC => "UNSPEC",         // 103
253                &DnsTypes::NID => "NID",               // 104
254                &DnsTypes::L32 => "L32",               // 105
255                &DnsTypes::L64 => "L64",               // 106
256                &DnsTypes::LP => "LP",                 // 107
257                &DnsTypes::EUI48 => "EUI48",           // 108
258                &DnsTypes::EUI64 => "EUI64",           // 109
259                &DnsTypes::TKEY => "TKEY",             // 249
260                &DnsTypes::TSIG => "TSIG",             // 250
261                &DnsTypes::IXFR => "IXFR",             // 251
262                &DnsTypes::AXFR => "AXFR",             // 252
263                &DnsTypes::MAILB => "MAILB",           // 253
264                &DnsTypes::MAILA => "MAILA",           // 254
265                &DnsTypes::ANY => "ANY",               // 255
266                &DnsTypes::URI => "URI",               // 256
267                &DnsTypes::CAA => "CAA",               // 257
268                &DnsTypes::AVC => "AVC",               // 258
269                &DnsTypes::DOA => "DOA",               // 259
270                &DnsTypes::AMTRELAY => "AMTRELAY",     // 260
271                &DnsTypes::TA => "TA",                 // 32768
272                &DnsTypes::DLV => "DLV",               // 32769
273                _ => "unknown",
274            }
275        )
276    }
277}
278
279/// Represents a DNS packet.
280/// Including its header and all the associated records.
281#[packet]
282pub struct Dns {
283    pub id: u16be,
284    pub is_response: u1,
285    #[construct_with(u4)]
286    pub opcode: OpCode,
287    pub is_authoriative: u1,
288    pub is_truncated: u1,
289    pub is_recursion_desirable: u1,
290    pub is_recursion_available: u1,
291    pub zero_reserved: u1,
292    pub is_answer_authenticated: u1,
293    pub is_non_authenticated_data: u1,
294    #[construct_with(u4)]
295    pub rcode: RetCode,
296    pub query_count: u16be,
297    pub response_count: u16be,
298    pub authority_rr_count: u16be,
299    pub additional_rr_count: u16be,
300    #[length_fn = "queries_length"]
301    pub queries: Vec<DnsQuery>,
302    #[length_fn = "responses_length"]
303    pub responses: Vec<DnsResponse>,
304    #[length_fn = "authority_length"]
305    pub authorities: Vec<DnsResponse>,
306    #[length_fn = "additional_length"]
307    pub additionals: Vec<DnsResponse>,
308    #[payload]
309    pub payload: Vec<u8>,
310}
311
312fn queries_length(packet: &DnsPacket) -> usize {
313    let base = 12;
314    let mut length = 0;
315    for _ in 0..packet.get_query_count() {
316        match DnsQueryPacket::new(&packet.packet()[base + length..]) {
317            Some(query) => length += query.packet_size(),
318            None => break,
319        }
320    }
321    length
322}
323
324fn responses_length(packet: &DnsPacket) -> usize {
325    let base = 12 + queries_length(packet);
326    let mut length = 0;
327    for _ in 0..packet.get_response_count() {
328        match DnsResponsePacket::new(&packet.packet()[base + length..]) {
329            Some(query) => length += query.packet_size(),
330            None => break,
331        }
332    }
333    length
334}
335
336fn authority_length(packet: &DnsPacket) -> usize {
337    let base = 12 + queries_length(packet) + responses_length(packet);
338    let mut length = 0;
339    for _ in 0..packet.get_authority_rr_count() {
340        match DnsResponsePacket::new(&packet.packet()[base + length..]) {
341            Some(query) => length += query.packet_size(),
342            None => break,
343        }
344    }
345    length
346}
347
348fn additional_length(packet: &DnsPacket) -> usize {
349    let base = 12 + queries_length(packet) + responses_length(packet) + authority_length(packet);
350    let mut length = 0;
351    for _ in 0..packet.get_additional_rr_count() {
352        match DnsResponsePacket::new(&packet.packet()[base + length..]) {
353            Some(query) => length += query.packet_size(),
354            None => break,
355        }
356    }
357    length
358}
359
360/// Represents an DNS operation code.
361/// <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5>
362#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
363pub enum OpCode {
364    Query,
365    InverseQuery,
366    Status,
367    Reserved,
368    Notify,
369    Update,
370    Dso,
371    Unassigned(u8),
372}
373
374impl PrimitiveValues for OpCode {
375    type T = (u8,);
376    fn to_primitive_values(&self) -> (u8,) {
377        match self {
378            Self::Query => (0,),
379            Self::InverseQuery => (1,),
380            Self::Status => (2,),
381            Self::Reserved => (3,),
382            Self::Notify => (4,),
383            Self::Update => (5,),
384            Self::Dso => (6,),
385            Self::Unassigned(n) => (*n,),
386        }
387    }
388}
389
390impl OpCode {
391    pub fn new(value: u8) -> Self {
392        match value {
393            0 => Self::Query,
394            1 => Self::InverseQuery,
395            2 => Self::Status,
396            3 => Self::Reserved,
397            4 => Self::Notify,
398            5 => Self::Update,
399            6 => Self::Dso,
400            _ => Self::Unassigned(value),
401        }
402    }
403}
404
405/// Represents an DNS return code.
406/// <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6>
407#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
408pub enum RetCode {
409    NoError,
410    FormErr,
411    ServFail,
412    NXDomain,
413    NotImp,
414    Refused,
415    YXDomain,
416    YXRRSet,
417    NXRRSet,
418    NotAuth,
419    NotZone,
420    Dsotypeni,
421    BadVers,
422    BadKey,
423    BadTime,
424    BadMode,
425    BadName,
426    BadAlg,
427    BadTrunc,
428    BadCookie,
429    Unassigned(u8),
430}
431
432impl PrimitiveValues for RetCode {
433    type T = (u8,);
434    fn to_primitive_values(&self) -> (u8,) {
435        match self {
436            Self::NoError => (0,),
437            Self::FormErr => (1,),
438            Self::ServFail => (2,),
439            Self::NXDomain => (3,),
440            Self::NotImp => (4,),
441            Self::Refused => (5,),
442            Self::YXDomain => (6,),
443            Self::YXRRSet => (7,),
444            Self::NXRRSet => (8,),
445            Self::NotAuth => (9,),
446            Self::NotZone => (10,),
447            Self::Dsotypeni => (11,),
448            Self::BadVers => (16,),
449            Self::BadKey => (17,),
450            Self::BadTime => (18,),
451            Self::BadMode => (19,),
452            Self::BadName => (20,),
453            Self::BadAlg => (21,),
454            Self::BadTrunc => (22,),
455            Self::BadCookie => (23,),
456            Self::Unassigned(n) => (*n,),
457        }
458    }
459}
460
461impl RetCode {
462    pub fn new(value: u8) -> Self {
463        match value {
464            0 => Self::NoError,
465            1 => Self::FormErr,
466            2 => Self::ServFail,
467            3 => Self::NXDomain,
468            4 => Self::NotImp,
469            5 => Self::Refused,
470            6 => Self::YXDomain,
471            7 => Self::YXRRSet,
472            8 => Self::NXRRSet,
473            9 => Self::NotAuth,
474            10 => Self::NotZone,
475            11 => Self::Dsotypeni,
476            16 => Self::BadVers,
477            17 => Self::BadKey,
478            18 => Self::BadTime,
479            19 => Self::BadMode,
480            20 => Self::BadName,
481            21 => Self::BadAlg,
482            22 => Self::BadTrunc,
483            23 => Self::BadCookie,
484            _ => Self::Unassigned(value),
485        }
486    }
487}
488
489/// DNS query packet structure.
490#[packet]
491pub struct DnsQuery {
492    #[length_fn = "qname_length"]
493    pub qname: Vec<u8>,
494    #[construct_with(u16be)]
495    pub qtype: DnsType,
496    #[construct_with(u16be)]
497    pub qclass: DnsClass,
498    #[payload]
499    pub payload: Vec<u8>,
500}
501
502fn qname_length(packet: &DnsQueryPacket) -> usize {
503    packet.packet().iter().take_while(|w| *w != &0).count() + 1
504}
505
506impl DnsQuery {
507    pub fn get_qname_parsed(&self) -> Result<String, Utf8Error> {
508        let name = &self.qname;
509        let mut qname = String::new();
510        let mut offset = 0;
511        loop {
512            let label_len = name[offset] as usize;
513            if label_len == 0 {
514                break;
515            }
516            if !qname.is_empty() {
517                qname.push('.');
518            }
519            match str::from_utf8(&name[offset + 1..offset + 1 + label_len]) {
520                Ok(label) => qname.push_str(label),
521                Err(e) => return Err(e),
522            }
523            offset += label_len + 1;
524        }
525        Ok(qname)
526    }
527}
528
529/// DNS response packet structure.
530#[packet]
531pub struct DnsResponse {
532    #[length_fn = "rname_length"]
533    pub rname: Vec<u8>,
534    #[construct_with(u16be)]
535    pub rtype: DnsType,
536    #[construct_with(u16be)]
537    pub rclass: DnsClass,
538    pub ttl: u32be,
539    pub data_len: u16be,
540    #[length = "data_len"]
541    pub data: Vec<u8>,
542    #[payload]
543    pub payload: Vec<u8>,
544}
545
546/// Parses and Returns the length of the rname field.
547fn rname_length(packet: &DnsResponsePacket) -> usize {
548    let mut offset = 0;
549    let mut size = 0;
550    loop {
551        let label_len = packet.packet()[offset] as usize;
552        if label_len == 0 {
553            size += 1;
554            break;
555        }
556        if label_len & 0xC0 == 0xC0 {
557            size += 2;
558            break;
559        }
560        size += label_len + 1;
561        offset += label_len + 1;
562    }
563    size
564}
565
566/// Parses the rname field of a DNS packet.
567pub fn parse_name(packet: &DnsPacket, coded_name: &Vec<u8>) -> Result<String, Utf8Error> {
568    // First follow the path in the rname, except if it starts with a C0
569    // then move to using the offsets from the start
570    let start = packet.packet();
571    let mut name = coded_name.as_slice();
572    let mut rname = String::new();
573    let mut offset: usize = 0;
574
575    loop {
576        let label_len: u16 = name[offset] as u16;
577        if label_len == 0 {
578            break;
579        }
580        if (label_len & 0xC0) == 0xC0 {
581            let offset1 = ((label_len & 0x3F) as usize) << 8;
582            let offset2 = name[offset + 1] as usize;
583            offset = offset1 + offset2;
584            // now change name
585            name = start;
586            continue;
587        }
588        if !rname.is_empty() {
589            rname.push('.');
590        }
591        match str::from_utf8(&name[offset + 1..offset + 1 + label_len as usize]) {
592            Ok(label) => rname.push_str(label),
593            Err(e) => return Err(e),
594        }
595        offset += label_len as usize + 1;
596    }
597    Ok(rname)
598}
599
600/// Represents a DNS TXT record.
601///
602/// TXT records hold descriptive text. The actual text is stored in the `text` field.
603#[packet]
604pub struct DnsRrTXT {
605    pub data_len: u8,
606    #[length = "data_len"]
607    pub text: Vec<u8>,
608    #[payload]
609    pub payload: Vec<u8>,
610}
611
612/// Represents a DNS SRV record.
613///
614/// SRV records are used to specify the location of services by providing a hostname and port number.
615#[packet]
616pub struct DnsRrSrv {
617    pub priority: u16be,
618    pub weight: u16be,
619    pub port: u16be,
620    #[length_fn = "target_length"]
621    pub target: Vec<u8>,
622    #[payload]
623    pub payload: Vec<u8>,
624}
625
626fn target_length(packet: &DnsRrSrvPacket) -> usize {
627    let mut offset = 6;
628    let mut size = 0;
629    loop {
630        let label_len = packet.packet()[offset] as usize;
631        if label_len == 0 {
632            size += 1;
633            break;
634        }
635        if label_len & 0xC0 == 0xC0 {
636            size += 2;
637            break;
638        }
639        size += label_len + 1;
640        offset += label_len + 1;
641    }
642    size
643}
644
645/// A structured representation of a Service Name (SRV record content).
646///
647/// Parses and holds components of an SRV record's target domain, which includes service instance, service type, protocol, and domain name.
648/// SRV record name
649#[derive(Debug)]
650pub struct SrvName {
651    pub instance: Option<String>,
652    pub service: Option<String>,
653    pub protocol: Option<String>,
654    pub domain: Option<String>,
655}
656
657impl SrvName {
658    pub fn new(name: &str) -> Self {
659        let parts: Vec<&str> = name.split('.').collect();
660        let (instance, service, protocol, domain) = match parts.as_slice() {
661            [instance, service, protocol, domain @ ..]
662                if service.starts_with('_') && protocol.starts_with('_') =>
663            {
664                (
665                    Some(String::from(*instance)),
666                    Some(String::from(*service)),
667                    Some(String::from(*protocol)),
668                    Some(String::from(domain.join("."))),
669                )
670            }
671            [service, protocol, domain @ ..]
672                if service.starts_with('_') && protocol.starts_with('_') =>
673            {
674                (
675                    None,
676                    Some(String::from(*service)),
677                    Some(String::from(*protocol)),
678                    Some(String::from(domain.join("."))),
679                )
680            }
681            [instance, service, protocol, domain @ ..] => (
682                Some(String::from(*instance)),
683                Some(String::from(*service)),
684                Some(String::from(*protocol)),
685                Some(String::from(domain.join("."))),
686            ),
687            _ => (None, None, None, None),
688        };
689
690        SrvName {
691            instance,
692            service,
693            protocol,
694            domain,
695        }
696    }
697}
698
699#[test]
700fn test_dns_query_packet() {
701    let packet = DnsPacket::new(b"\x1e\xcb\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x0a\x63\x6c\x6f\x75\x64\x66\x6c\x61\x72\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00").unwrap();
702    assert_eq!(packet.get_id(), 7883);
703    assert_eq!(packet.get_is_response(), 0);
704    assert_eq!(packet.get_opcode(), OpCode::Query);
705    assert_eq!(packet.get_is_authoriative(), 0);
706    assert_eq!(packet.get_is_truncated(), 0);
707    assert_eq!(packet.get_is_recursion_desirable(), 1);
708    assert_eq!(packet.get_is_recursion_available(), 0);
709    assert_eq!(packet.get_zero_reserved(), 0);
710    assert_eq!(packet.get_rcode(), RetCode::NoError);
711    assert_eq!(packet.get_query_count(), 1);
712    assert_eq!(packet.get_response_count(), 0);
713    assert_eq!(packet.get_authority_rr_count(), 0);
714    assert_eq!(packet.get_additional_rr_count(), 1);
715    assert_eq!(packet.get_queries().len(), 1);
716    assert_eq!(
717        packet.get_queries()[0]
718            .get_qname_parsed()
719            .unwrap_or(String::new()),
720        "cloudflare.com"
721    );
722    assert_eq!(packet.get_queries()[0].qtype, DnsTypes::A);
723    assert_eq!(packet.get_queries()[0].qclass, DnsClasses::IN);
724    assert_eq!(packet.get_responses().len(), 0);
725    assert_eq!(packet.get_authorities().len(), 0);
726    assert_eq!(packet.get_additionals().len(), 1);
727}
728
729#[test]
730fn test_dns_reponse_packet() {
731    let packet = DnsPacket::new(b"\x1e\xcb\x81\xa0\x00\x01\x00\x02\x00\x00\x00\x01\x0a\x63\x6c\x6f\x75\x64\x66\x6c\x61\x72\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\xc4\x00\x04h\x10\x85\xe5\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\xc4\x00\x04h\x10\x84\xe5\x00\x00)\x04\xd0\x00\x00\x00\x00\x00\x00").unwrap();
732    assert_eq!(packet.get_id(), 7883);
733    assert_eq!(packet.get_is_response(), 1);
734    assert_eq!(packet.get_opcode(), OpCode::Query);
735    assert_eq!(packet.get_is_authoriative(), 0);
736    assert_eq!(packet.get_is_truncated(), 0);
737    assert_eq!(packet.get_is_recursion_desirable(), 1);
738    assert_eq!(packet.get_is_recursion_available(), 1);
739    assert_eq!(packet.get_zero_reserved(), 0);
740    assert_eq!(packet.get_rcode(), RetCode::NoError);
741    assert_eq!(packet.get_query_count(), 1);
742    assert_eq!(packet.get_response_count(), 2);
743    assert_eq!(packet.get_authority_rr_count(), 0);
744    assert_eq!(packet.get_additional_rr_count(), 1);
745    assert_eq!(packet.get_queries().len(), 1);
746    assert_eq!(
747        packet.get_queries()[0]
748            .get_qname_parsed()
749            .unwrap_or(String::new()),
750        "cloudflare.com"
751    );
752    assert_eq!(packet.get_queries()[0].qtype, DnsTypes::A);
753    assert_eq!(packet.get_queries()[0].qclass, DnsClasses::IN);
754    assert_eq!(packet.get_responses().len(), 2);
755    assert_eq!(packet.get_responses()[0].rtype, DnsTypes::A);
756    assert_eq!(packet.get_responses()[0].rclass, DnsClasses::IN);
757    assert_eq!(packet.get_responses()[0].ttl, 196);
758    assert_eq!(packet.get_responses()[0].data_len, 4);
759    assert_eq!(
760        packet.get_responses()[0].data.as_slice(),
761        [104, 16, 133, 229]
762    );
763    assert_eq!(packet.get_authorities().len(), 0);
764    assert_eq!(packet.get_additionals().len(), 1);
765}
766
767#[test]
768fn test_mdns_response() {
769    let data = b"\x00\x00\x84\x00\x00\x00\x00\x04\x00\x00\x00\x00\x0b\x5f\x61\x6d\x7a\x6e\x2d\x61\x6c\x65\x78\x61\x04\x5f\x74\x63\x70\x05\x6c\x6f\x63\x61\x6c\x00\x00\x0c\x00\x01\x00\x00\x11\x94\x00\x0b\x08\x5f\x73\x65\x72\x76\x69\x63\x65\xc0\x0c\xc0\x2e\x00\x10\x80\x01\x00\x00\x11\x94\x00\x0a\x09\x76\x65\x72\x73\x69\x6f\x6e\x3d\x31\xc0\x2e\x00\x21\x80\x01\x00\x00\x00\x78\x00\x1d\x00\x00\x00\x00\x19\x8f\x14\x61\x76\x73\x2d\x66\x66\x72\x65\x67\x2d\x31\x36\x35\x34\x34\x37\x35\x36\x38\x33\xc0\x1d\xc0\x61\x00\x01\x80\x01\x00\x00\x00\x78\x00\x04\xc0\xa8\x01\x06";
770    let packet = DnsPacket::new(data).expect("Failed to parse dns response");
771    assert_eq!(packet.get_id(), 0);
772    assert_eq!(packet.get_is_response(), 1);
773    assert_eq!(packet.get_opcode(), OpCode::Query);
774    assert_eq!(packet.get_is_authoriative(), 1);
775    assert_eq!(packet.get_is_truncated(), 0);
776    assert_eq!(packet.get_is_recursion_desirable(), 0);
777    assert_eq!(packet.get_is_recursion_available(), 0);
778    assert_eq!(packet.get_zero_reserved(), 0);
779    assert_eq!(packet.get_rcode(), RetCode::NoError);
780    assert_eq!(packet.get_query_count(), 0);
781    assert_eq!(packet.get_response_count(), 4);
782    assert_eq!(packet.get_authority_rr_count(), 0);
783    assert_eq!(packet.get_additional_rr_count(), 0);
784    assert_eq!(packet.get_responses().len(), 4);
785    let responses = packet.get_responses();
786    // RR #1
787    assert_eq!(
788        parse_name(&packet, &responses[0].rname).unwrap_or(String::new()),
789        "_amzn-alexa._tcp.local"
790    );
791    assert_eq!(responses[0].rtype, DnsTypes::PTR);
792    assert_eq!(responses[0].rclass, DnsClasses::IN);
793    assert_eq!(responses[0].ttl, 4500);
794    assert_eq!(responses[0].data_len, 11);
795    assert_eq!(
796        parse_name(&packet, &responses[0].data).unwrap_or(String::new()),
797        "_service._amzn-alexa._tcp.local"
798    );
799    // RR #2
800    assert_eq!(
801        parse_name(&packet, &responses[1].rname).unwrap_or(String::new()),
802        "_service._amzn-alexa._tcp.local"
803    );
804    assert_eq!(responses[1].rtype, DnsTypes::TXT);
805    assert_eq!(responses[1].ttl, 4500);
806    assert_eq!(responses[1].data_len, 10);
807    let text_rr = DnsRrTXTPacket::new(&responses[1].data).unwrap();
808    assert_eq!(text_rr.get_data_len(), 9);
809    assert_eq!(String::from_utf8(text_rr.get_text()).unwrap(), "version=1");
810    // RR #3
811    let srv_name = parse_name(&packet, &responses[2].rname).unwrap_or(String::new());
812    assert_eq!(srv_name, "_service._amzn-alexa._tcp.local");
813    assert_eq!(responses[2].rtype, DnsTypes::SRV);
814    assert_eq!(responses[2].data_len, 29);
815    let srv_rr = DnsRrSrvPacket::new(&responses[2].data).unwrap();
816    assert_eq!(srv_rr.get_priority(), 0);
817    assert_eq!(srv_rr.get_weight(), 0);
818    assert_eq!(srv_rr.get_port(), 6543);
819    assert_eq!(
820        parse_name(&packet, &srv_rr.get_target()).unwrap_or(String::new()),
821        "avs-ffreg-1654475683.local"
822    );
823    let srv = SrvName::new(&srv_name);
824    assert_eq!(srv.instance, Some(String::from("_service")));
825    assert_eq!(srv.service, Some(String::from("_amzn-alexa")));
826    assert_eq!(srv.protocol, Some(String::from("_tcp")));
827    assert_eq!(srv.domain, Some(String::from("local")));
828    // RR #4
829    assert_eq!(responses[3].rtype, DnsTypes::A);
830    assert_eq!(responses[3].data.as_slice(), [192, 168, 1, 6]);
831}