cdns_rs/sync/
common.rs

1/*-
2 * cdns-rs - a simple sync/async DNS query library
3 * 
4 * Copyright (C) 2020  Aleksandr Morozov
5 * 
6 * Copyright 2025 Aleksandr Morozov
7 * 
8 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
9 * the European Commission - subsequent versions of the EUPL (the "Licence").
10 * 
11 * You may not use this work except in compliance with the Licence.
12 * 
13 * You may obtain a copy of the Licence at:
14 * 
15 *    https://joinup.ec.europa.eu/software/page/eupl
16 * 
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
19 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20 * Licence for the specific language governing permissions and limitations
21 * under the Licence.
22 */
23
24/*
25                                1  1  1  1  1  1
26  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
27+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
28                    | ID |
29+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
30|QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
31+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
32                    | QDCOUNT |
33+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
34                    | ANCOUNT |
35+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
36                    | NSCOUNT |
37+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
38                    | ARCOUNT |
39+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
40
41                                1  1  1  1  1  1
42  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
43+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
44|                                               |
45/                       QNAME                   /
46/                                               /
47+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
48|                       QTYPE                   |
49+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
50|                       QCLASS                  |
51+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
52
53*/
54
55/// This file contains the common structures and constants of DNS protocol and 
56/// other parts of the code. Also it contains a parser from/to network packet.
57
58use std::{io::{Cursor, Read, Write}, mem::size_of, net::{Ipv4Addr, Ipv6Addr}};
59use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
60
61use crate::error::*;
62use crate::{internal_error, internal_error_map};
63
64
65pub use crate::common::*;
66
67/// Verifies the sequence of labels coding and copies data from pkt to
68/// buffer.
69/// 
70/// # Arguments
71/// 
72/// * `pkt` - a reference to the [Cursor]
73/// 
74/// # Returns 
75/// 
76/// * [CDnsResult] with data copied to new [Vec][u8]
77pub 
78fn pkt2name(pkt: &mut Cursor<&[u8]>) -> CDnsResult<Vec<u8>>
79{
80    let qname_s = pkt.position() as usize;
81
82    loop
83    {
84        let cnt = pkt.read_u8().map_err(map_read_err)?;
85
86        if cnt == 0
87        {
88            break;
89        }
90        else
91        {
92            for _ in 0..cnt
93            {
94                let c = pkt.read_u8().map_err(map_read_err)?;
95                if c == 0
96                {
97                    internal_error!(
98                        CDnsErrorType::DnsResponse, 
99                        "incorrectly encoded QNAME in response, found '0' at offset: '{}'",
100                        pkt.position()
101                    );
102                }
103            }
104        }
105    }
106
107    // temporary store the position of the cursor
108    let cur_pos = pkt.position() as usize;
109
110    if (cur_pos - qname_s) <= 1
111    {
112        internal_error!(
113            CDnsErrorType::DnsResponse, 
114            "read name is too short"
115        );
116    }
117
118    /*pkt.set_position(qname_s);
119    
120    // creating buffer for QNAME
121    let mut req: Vec<u8> = vec![0_u8; (cur_pos - qname_s) as usize];
122    pkt.read_exact(&mut req).map_err(map_read_err)?;
123
124    // restore position
125    pkt.set_position(cur_pos);*/
126    
127
128    return Ok(pkt.get_ref()[qname_s..cur_pos].to_vec());
129}
130
131
132/// This function converts a QNAME (encoded domains) to the String FQDN.
133pub 
134fn name2str(pkt: &mut Cursor<&[u8]>, mut opt_rdlen: Option<u16>) -> CDnsResult<String>
135{
136    let cur_in_pos = pkt.position();
137    //let mut p_comp: u8 = 0;
138    let mut comp: u8;
139    let mut output: Vec<String> = Vec::with_capacity(6);
140
141    loop
142    {
143        if let Some(rdlen) = opt_rdlen
144        {
145            if pkt.position() - cur_in_pos >= rdlen as u64
146            {
147                return Ok(output.join("."));
148            }
149        }
150
151        // reading word from pkt to detect the type of message
152       // p_comp = comp;
153        comp = pkt.read_u8().map_err(map_read_err)?;
154
155        /*if p_comp > 0 && comp > 0
156        {
157            output.push('.');
158        }*/
159
160        let msb = comp & 0xC0;
161
162        if msb == 0xC0
163        {
164            if opt_rdlen.is_none() == true
165            {
166                opt_rdlen = Some(2);
167            }
168
169            let offset1: u16 = ((comp & !0xC0) as u16) << 8;// | pkt.read_u8().map_err(map_read_err)? as u16;
170            let offset2: u16 = pkt.read_u8().map_err(map_read_err)? as u16;
171
172            let offset = offset1 | offset2;
173
174            //println!("debug: 1: {} 2: {} offset: {}", offset1, offset2, offset);
175
176            // C0 0...
177            if offset as usize >= pkt.get_ref().len()
178            {
179                internal_error!(
180                    CDnsErrorType::DnsResponse, 
181                    "incoreclty formated packet: offset: '{}' > len: '{}'", 
182                    offset, pkt.get_ref().len()
183                );
184            }
185
186            // save current cursor position
187            let cur_pos = pkt.position();
188            pkt.set_position(offset as u64);
189
190            // converting the name to FQDN
191            output.push(name2str(pkt, None)?);
192            //output.push_str(cache.get(&offset).ok_or_else(|| internal_error_map!(CDnsErrorType::InternalError, "cache mismatch"))?);
193
194            // restoring cursor positon
195            pkt.set_position(cur_pos);
196        }
197        else if msb == 0x00 
198        {
199            if comp == 0
200            {
201                if let Some(rdlen) = opt_rdlen
202                {
203                    let dif = pkt.position() - cur_in_pos;
204
205                    if rdlen as u64 != dif
206                    {
207                        internal_error!(CDnsErrorType::DnsResponse, "incorrect rdlen: '{}', exp: '{}'", rdlen, dif);
208                    }
209                }
210
211                return Ok(output.join("."));
212            }
213            else
214            {
215                let mut tmp: String = String::with_capacity(comp as usize);
216
217                for _ in 0..comp
218                {
219                    let c = pkt.read_u8().map_err(map_read_err)?;
220                    if c == 0
221                    {
222                        internal_error!(CDnsErrorType::DnsResponse, "protocol violation, incorrectly encoded QNAME in response");
223                    }
224
225                    tmp.push(c as char);
226                }
227
228                output.push(tmp);
229            }
230        }
231        else
232        {
233            internal_error!(CDnsErrorType::DnsResponse, "incorrect compression: {:x}", msb);
234        }
235
236    } // loop
237}
238
239
240impl DnsRdata
241{
242    /// Reads the RData from pkt and converts it into QType container
243    /// 
244    /// # Arguments
245    /// 
246    /// * `pkt` - a [Cursor] mutable reference to raw DNS reply data
247    /// 
248    /// * `rlen` - a length of RData section
249    /// 
250    /// * `qtype` - a [QType] reference to the response answer Qtype
251    /// 
252    /// # Returns
253    /// 
254    /// * [CDnsResult] with Self or error
255    fn convert(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: &QType) -> CDnsResult<Self>
256    {
257        match &qtype
258        {
259            QType::A => 
260            {
261                // read 4 bytes (octets)
262                if rlen != 4
263                {
264                    internal_error!(CDnsErrorType::DnsResponse, "ipv4 len expected: '4', got: '{}'", rlen);
265                }
266
267                let ip = pkt.read_u32::<BigEndian>().map_err(map_read_err)?;
268                return Ok(Self::A{ ip: Ipv4Addr::from(ip) });
269            },
270            QType::AAAA => 
271            {
272                // read rlen bytes (octets)
273                if rlen != 16
274                {
275                    internal_error!(CDnsErrorType::DnsResponse, "ipv6 len expected: '16', got: '{}'", rlen);
276                }
277
278                let ip = pkt.read_u128::<BigEndian>().map_err(map_read_err)?;
279                return Ok(Self::AAAA{ ip: Ipv6Addr::from(ip) });
280            },
281            QType::MX =>
282            {
283                return Ok(
284                    Self::MX
285                    { 
286                        preference: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
287                        exchange: name2str(pkt, Some(rlen - 2))?,
288                    }
289                );
290            },
291            QType::CNAME =>
292            {
293                return Ok(
294                    Self::CNAME{ fqdn: name2str(pkt, Some(rlen))? }
295                );
296            },
297            QType::PTR =>
298            {
299                return Ok(
300                    Self::PTR{ fqdn: name2str(pkt, Some(rlen))? }
301                );
302            },
303            QType::NS =>
304            {
305                return Ok(
306                    Self::NS{ fqdn: name2str(pkt, Some(rlen))? }
307                );
308            },
309            QType::SOA =>
310            {
311                return Ok(
312                    Self::SOA
313                    {   
314                        soa:
315                            DnsSoa
316                            {
317                                // primary name server
318                                pnm: name2str(pkt, None)?, 
319                                // responsible authority mailbox
320                                ram: name2str(pkt, None)?,
321                                // serial number
322                                serial: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
323                                // refresh interval
324                                interv_refr: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
325                                // retry interval
326                                interv_retry: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
327                                // expire limit
328                                expire_limit: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
329                                // minimum TTL
330                                min_ttl: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
331                            }
332                    }
333                );
334            },
335            _ =>
336            {
337                let ipos = pkt.position();
338                let npos: u64 = pkt.position() + rlen as u64;
339                let buf_size = npos - ipos;
340             
341                let mut data: Vec<u8> = vec![0_u8; buf_size as usize];//pkt.get_ref()[s..s+rlen as usize].to_vec();
342                pkt.read_exact(&mut data).map_err(map_read_err)?;
343             
344                pkt.set_position(npos);
345                //   pkt.set_position((s+rlen as usize) as u64);
346                /*QType::MD | QType::MF | QType::SOA | QType::MB | QType::MG | 
347            QType::MR | QType::NULL | QType::WKS | QType::HINFO | QType::TXT |
348            QType::AFSDB | QType::KEY | QType::CERT | QType::DS | QType::RRSIG |
349            QType::NSEC | QType::DNSKEY | QType::NSEC3 | QType::NSEC3PARAM | 
350            QType::CDS | QType::CDNSKEY | QType::OPENPGPKEY  */
351
352                return Ok(Self::UNKNOWN{ data: data });
353            }
354        }
355    }
356}
357
358
359impl DnsResponsePayload
360{
361    fn new(pkt: &mut Cursor<&[u8]>) -> CDnsResult<Self>
362    {
363        let name = name2str(pkt, None)?;
364        let qtype = QType::u16_to_type(pkt.read_u16::<BigEndian>().map_err(map_read_err)?)?;
365        let qclass = QClass::u16_to_class(pkt.read_u16::<BigEndian>().map_err(map_read_err)?)?;
366        let ttl = pkt.read_i32::<BigEndian>().map_err(map_read_err)?;
367        let rdlength = pkt.read_u16::<BigEndian>().map_err(map_read_err)?;
368        let rdata = DnsRdata::convert(pkt, rdlength, &qtype)?;
369
370        return Ok(
371            DnsResponsePayload
372            {
373                name: name,
374                dtype: qtype,
375                class: qclass,
376                ttl: ttl,
377                rdlength: rdlength,
378                rdata: rdata,
379            }
380        );
381    }
382}
383
384
385impl TryFrom<&[u8]> for DnsRequestAnswer
386{
387    type Error = CDnsError;
388
389    fn try_from(value: &[u8]) -> Result<Self, Self::Error> 
390    {
391        return DnsRequestAnswer::parse(value);
392    }
393}
394
395impl DnsRequestAnswer
396{
397    /// Parses the raw data from received packet.
398    /// 
399    /// # Arguments
400    /// 
401    /// * `ans` - the received data
402    fn parse(ans: &[u8]) -> CDnsResult<Self>
403    {
404        // creating cursor
405        let mut pkt = Cursor::new(ans);
406
407        // parsing header
408        let id = pkt.read_u16::<BigEndian>().map_err(map_read_err)?;
409
410        let status = pkt.read_u16::<BigEndian>().map_err(map_read_err)?;
411
412        let header = 
413            DnsHeader
414            {
415                id: id,
416                status: StatusBits::from_bits(status).ok_or_else(|| 
417                    internal_error_map!(CDnsErrorType::DnsResponse, "unknown status bits: '{}'", status)
418                )?,
419                qdcount: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
420                ancount: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
421                nscount: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
422                arcount: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
423            };
424
425        // todo!("what about truncated messages");
426
427        // check if there is no more than 1 answer
428        if header.status.contains(StatusBits::QR_RESP) == false
429        {
430            internal_error!(
431                CDnsErrorType::DnsResponse, 
432                "incorret QR flag in STATUS register of response, expected 1 got 0"
433            );
434        }
435       /*else if header.ancount > 1
436        {
437            internal_error!(
438                CDnsErrorType::DnsRespose, 
439                "too many responses = '{}', multiple responses are not supported", 
440                header.ancount
441            );
442        }*/
443        /*else if header.ancount == 0 && header.status.contains(StatusBits::RESP_NOERROR) == true
444        {
445            internal_error!(CDnsErrorType::DnsRespose, "answer count: 0 while response contains no error");
446        }*/
447        
448        // reading request
449        //let cur_offset = pkt.position();
450    
451        let request = 
452            DnsRequestPayload
453            {
454                qname: pkt2name(&mut pkt)?,
455                qtype: QType::u16_to_qtype(pkt.read_u16::<BigEndian>().map_err(map_read_err)?)?,
456                qclass: QClass::u16_to_qclass(pkt.read_u16::<BigEndian>().map_err(map_read_err)?)?,
457            };
458
459        let mut an_list: Vec<DnsResponsePayload> = Vec::with_capacity(header.ancount as usize);
460        let mut rr_list: Vec<DnsResponsePayload> = Vec::with_capacity(header.arcount as usize);
461        let mut ns_list: Vec<DnsResponsePayload> = Vec::with_capacity(header.nscount as usize);
462
463        for _ in 0..header.ancount
464        {
465            an_list.push(DnsResponsePayload::new(&mut pkt)?);
466        }
467        
468        // reading authoritative section if any
469        for _ in 0..header.nscount
470        {
471            ns_list.push(DnsResponsePayload::new(&mut pkt)?);
472        }
473
474        // reading additional if any
475        for _ in 0..header.arcount
476        {
477            rr_list.push(DnsResponsePayload::new(&mut pkt)?);
478        }
479
480        return Ok(
481            DnsRequestAnswer
482            {
483                header: header,
484                request: request,
485                response: an_list,
486                additional: rr_list,
487                authoratives: ns_list,
488            }
489        );
490    }
491}
492
493impl DnsRequestHeader
494{
495    pub 
496    fn to_bytes(&self) -> CDnsResult<Vec<u8>>
497    {
498        let pkt_size: usize = size_of::<DnsHeader>() + self.payload.qname.len() + 4; 
499
500        let mut pkt = Cursor::new(vec![0_u8; pkt_size]);
501
502        pkt.write_u16::<BigEndian>(self.header.id).map_err(map_read_err)?;
503        pkt.write_u16::<BigEndian>(self.header.status.bits()).map_err(map_read_err)?;
504        pkt.write_u16::<BigEndian>(self.header.qdcount).map_err(map_read_err)?;
505        pkt.write_u16::<BigEndian>(self.header.ancount).map_err(map_read_err)?;
506        pkt.write_u16::<BigEndian>(self.header.nscount).map_err(map_read_err)?;
507        pkt.write_u16::<BigEndian>(self.header.arcount).map_err(map_read_err)?;
508
509        //for p in self.payload.iter()
510        //{
511            pkt.write(self.payload.qname.as_slice()).map_err(map_read_err)?;
512            pkt.write_u16::<BigEndian>(self.payload.qtype.into()).map_err(map_read_err)?;
513            pkt.write_u16::<BigEndian>(self.payload.qclass.into()).map_err(map_read_err)?;
514        //}
515
516        return Ok(pkt.into_inner());
517    }
518}
519
520
521#[test]
522fn test_pkt2name()
523{
524    use std::time::Instant;
525
526    fn prepare(d: &[u8]) -> Cursor<&[u8]>
527    {
528       // let vd = d.to_vec();
529
530        return Cursor::new(d);
531    }
532
533    let t1 = b"\x03\x64\x6e\x73\x06\x67\x6f\x6f\x67\x6c\x65\x00";
534
535    let mut cur1 = prepare(t1);
536
537    let now = Instant::now();
538    let r1 = pkt2name(&mut cur1);
539    let elapsed = now.elapsed();
540    println!("Elapsed: {:.2?}", elapsed);
541
542    assert_eq!(r1.is_ok(), true);
543    assert_eq!(r1.as_ref().unwrap(), t1);
544
545    let t2 = 
546    b"\x0f\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
547    \x30\x30\x03\x6e\x65\x74\x00";
548
549    let mut cur2 = prepare(t2);
550    let now = Instant::now();
551    let r2 = pkt2name(&mut cur2);
552    let elapsed = now.elapsed();
553    println!("Elapsed: {:.2?}", elapsed);
554
555    assert_eq!(r2.is_ok(), true);
556    assert_eq!(r2.as_ref().unwrap(), t2);
557
558    let t3 = 
559    b"\x0f\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
560    \x30\x30\x03\x6e\x65\x74";
561
562    let r3 = pkt2name(&mut prepare(t3));
563
564    assert_eq!(r3.is_ok(), false);
565
566    let t4 = 
567    b"\x10\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
568    \x30\x30\x03\x6e\x65\x74";
569
570    let r4 = pkt2name(&mut prepare(t4));
571
572    assert_eq!(r4.is_ok(), false);
573}
574
575#[test]
576fn test_qname2str()
577{
578    use std::time::Instant;
579
580    fn prepare(d: &[u8]) -> Cursor<&[u8]>
581    {
582        //let vd = d.to_vec();
583
584        return Cursor::new(d);
585    }
586
587    let t1 = b"\x03\x64\x6e\x73\x06\x67\x6f\x6f\x67\x6c\x65\x00";
588
589    let mut cur1 = prepare(t1);
590    let now = Instant::now();
591    let r1 = name2str(&mut cur1, None);
592    let elapsed = now.elapsed();
593    println!("Elapsed: {:.2?}", elapsed);
594
595    assert_eq!(r1.is_ok(), true);
596    assert_eq!(r1.as_ref().unwrap(), "dns.google");
597
598    let t2 = 
599    b"\x0f\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
600    \x30\x30\x03\x6e\x65\x74\x00";
601
602    let mut cur2 = prepare(t2);
603
604    let now = Instant::now();
605    let r2 = name2str(&mut cur2, None);
606    let elapsed = now.elapsed();
607    println!("Elapsed: {:.2?}", elapsed);
608
609    assert_eq!(r2.is_ok(), true);
610    assert_eq!(r2.as_ref().unwrap(), "mad07s09-in-x0e.1e100.net");
611
612    let t3 = 
613    b"\x0f\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
614    \x30\x30\x03\x6e\x65\x74";
615
616    let r3 = name2str(&mut prepare(t3), None);
617
618    assert_eq!(r3.is_ok(), false);
619
620    let t4 = 
621    b"\x10\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
622    \x30\x30\x03\x6e\x65\x74";
623
624    let r4 = name2str(&mut prepare(t4), None);
625
626    assert_eq!(r4.is_ok(), false);
627}
628
629#[test]
630fn test_response_parser()
631{
632    use std::time::Instant;
633
634    let pkt = 
635    b"\x15\xc8\x81\x80\x00\x01\x00\x01\x00\x00\x00\x02\x01\x38\x01\x38\
636    \x01\x38\x01\x38\x07\x69\x6e\x2d\x61\x64\x64\x72\x04\x61\x72\x70\
637    \x61\x00\x00\x0c\x00\x01\xc0\x0c\x00\x0c\x00\x01\x00\x01\x35\xf0\
638    \x00\x0c\x03\x64\x6e\x73\x06\x67\x6f\x6f\x67\x6c\x65\x00\xc0\x32\
639    \x00\x01\x00\x01\x00\x00\x00\xe8\x00\x04\x08\x08\x08\x08\xc0\x32\
640    \x00\x01\x00\x01\x00\x00\x00\xe8\x00\x04\x08\x08\x04\x04";
641
642    let dummy = 
643        DnsRequestHeader
644        {
645            header: DnsHeader {id: 0x15c8, ..Default::default()},
646            payload: DnsRequestPayload
647                {
648                    qname: b"\x01\x38\x01\x38\x01\x38\x01\x38\x07\x69\x6e\x2d\x61\x64\x64\x72\x04\x61\x72\x70\x61\x00".to_vec(),
649                    qtype: QType::PTR,
650                    qclass: QClass::IN, 
651                }
652        };
653
654    let now = Instant::now();
655    let ans = DnsRequestAnswer::try_from(pkt.as_slice());
656    let elapsed = now.elapsed();
657    println!("Elapsed: {:.2?}", elapsed);
658
659    assert_eq!(ans.is_ok(), true, "{}", ans.err().unwrap());
660    
661    let ans = ans.unwrap();
662
663    let verif = ans.verify(&dummy);
664    assert_eq!(verif.is_ok(), true, "{}", verif.err().unwrap());
665
666    assert_eq!(ans.header.id, 0x15c8);
667    assert_eq!(ans.header.ancount, 1);
668    assert_eq!(ans.header.arcount, 2);
669    assert_eq!(ans.header.nscount, 0);
670    assert_eq!(ans.header.qdcount, 1);
671
672    assert_eq!(ans.response[0].rdata, DnsRdata::PTR{ fqdn: "dns.google".to_string() });
673    assert_eq!(ans.response[0].name, "8.8.8.8.in-addr.arpa".to_string());
674    assert_eq!(ans.response[0].dtype, QType::PTR);
675    assert_eq!(ans.response[0].class, QClass::IN);
676    assert_eq!(ans.response[0].ttl, 79344);
677    assert_eq!(ans.response[0].rdlength, 12);
678
679    assert_eq!(ans.additional[0].rdata, DnsRdata::A{ ip: "8.8.8.8".parse().unwrap() });
680    assert_eq!(ans.additional[0].name, "dns.google".to_string());
681    assert_eq!(ans.additional[0].ttl, 232);
682    assert_eq!(ans.additional[0].dtype, QType::A);
683    assert_eq!(ans.additional[0].class, QClass::IN);
684    assert_eq!(ans.additional[1].rdata, DnsRdata::A{ ip: "8.8.4.4".parse().unwrap() });
685    assert_eq!(ans.additional[1].name, "dns.google".to_string());
686    assert_eq!(ans.additional[1].ttl, 232);
687    assert_eq!(ans.additional[1].dtype, QType::A);
688    assert_eq!(ans.additional[1].class, QClass::IN);
689}
690
691#[test]
692fn test_response2()
693{
694    use std::time::Instant;
695
696    let pkt = 
697    b"\x4b\x38\x81\x80\x00\x01\x00\x05\x00\x00\x00\x00\x05\x67\x6d\x61\
698    \x69\x6c\x03\x63\x6f\x6d\x00\x00\x0f\x00\x01\xc0\x0c\x00\x0f\x00\
699    \x01\x00\x00\x0b\x55\x00\x1b\x00\x05\x0d\x67\x6d\x61\x69\x6c\x2d\
700    \x73\x6d\x74\x70\x2d\x69\x6e\x01\x6c\x06\x67\x6f\x6f\x67\x6c\x65\
701    \xc0\x12\xc0\x0c\x00\x0f\x00\x01\x00\x00\x0b\x55\x00\x09\x00\x0a\
702    \x04\x61\x6c\x74\x31\xc0\x29\xc0\x0c\x00\x0f\x00\x01\x00\x00\x0b\
703    \x55\x00\x09\x00\x1e\x04\x61\x6c\x74\x33\xc0\x29\xc0\x0c\x00\x0f\
704    \x00\x01\x00\x00\x0b\x55\x00\x09\x00\x14\x04\x61\x6c\x74\x32\xc0\
705    \x29\xc0\x0c\x00\x0f\x00\x01\x00\x00\x0b\x55\x00\x09\x00\x28\x04\
706    \x61\x6c\x74\x34\xc0\x29";
707
708    let dummy = 
709        DnsRequestHeader
710        {
711            header: DnsHeader {id: 0x4b38, ..Default::default()},
712            payload: DnsRequestPayload
713                {
714                    qname: b"\x05\x67\x6d\x61\x69\x6c\x03\x63\x6f\x6d\x00".to_vec(),
715                    qtype: QType::MX,
716                    qclass: QClass::IN, 
717                }
718        };
719
720    let now = Instant::now();
721    let ans = DnsRequestAnswer::try_from(pkt.as_slice());
722    let elapsed = now.elapsed();
723    println!("Elapsed: {:.2?}", elapsed);
724
725    assert_eq!(ans.is_ok(), true, "{}", ans.err().unwrap());
726    let ans = ans.unwrap();
727
728    let verif = ans.verify(&dummy);
729    assert_eq!(verif.is_ok(), true, "{}", verif.err().unwrap());
730
731    assert_eq!(ans.header.id, 0x4b38);
732    assert_eq!(ans.header.ancount, 5);
733    assert_eq!(ans.header.arcount, 0);
734    assert_eq!(ans.header.nscount, 0);
735    assert_eq!(ans.header.qdcount, 1);
736
737    assert_eq!(ans.response[0].rdata, DnsRdata::MX{ preference: 5, exchange: "gmail-smtp-in.l.google.com".to_string() });
738    assert_eq!(ans.response[0].name, "gmail.com".to_string());
739    assert_eq!(ans.response[0].dtype, QType::MX);
740    assert_eq!(ans.response[0].class, QClass::IN);
741    assert_eq!(ans.response[0].ttl, 2901);
742    assert_eq!(ans.response[0].rdlength, 27);
743
744    assert_eq!(ans.response[1].rdata, DnsRdata::MX{ preference: 10, exchange: "alt1.gmail-smtp-in.l.google.com".to_string() });
745    assert_eq!(ans.response[1].name, "gmail.com".to_string());
746    assert_eq!(ans.response[1].dtype, QType::MX);
747    assert_eq!(ans.response[1].class, QClass::IN);
748    assert_eq!(ans.response[1].ttl, 2901);
749    assert_eq!(ans.response[1].rdlength, 9);
750
751    assert_eq!(ans.response[2].rdata, DnsRdata::MX{ preference: 30, exchange: "alt3.gmail-smtp-in.l.google.com".to_string() });
752    assert_eq!(ans.response[2].name, "gmail.com".to_string());
753    assert_eq!(ans.response[2].dtype, QType::MX);
754    assert_eq!(ans.response[2].class, QClass::IN);
755    assert_eq!(ans.response[2].ttl, 2901);
756    assert_eq!(ans.response[2].rdlength, 9);
757
758    assert_eq!(ans.response[3].rdata, DnsRdata::MX{ preference: 20, exchange: "alt2.gmail-smtp-in.l.google.com".to_string() });
759    assert_eq!(ans.response[3].name, "gmail.com".to_string());
760    assert_eq!(ans.response[3].dtype, QType::MX);
761    assert_eq!(ans.response[3].class, QClass::IN);
762    assert_eq!(ans.response[3].ttl, 2901);
763    assert_eq!(ans.response[3].rdlength, 9);
764
765    assert_eq!(ans.response[4].rdata, DnsRdata::MX{ preference: 40, exchange: "alt4.gmail-smtp-in.l.google.com".to_string() });
766    assert_eq!(ans.response[4].name, "gmail.com".to_string());
767    assert_eq!(ans.response[4].dtype, QType::MX);
768    assert_eq!(ans.response[4].class, QClass::IN);
769    assert_eq!(ans.response[4].ttl, 2901);
770    assert_eq!(ans.response[4].rdlength, 9);
771}
772
773
774#[test]
775fn test_response3()
776{
777    use std::time::Instant;
778
779    let pkt = 
780b"\xd0\x79\x81\x80\x00\x01\x00\x17\x00\x00\x00\x00\x06\x72\
781\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\x00\xff\x00\x01\xc0\x0c\x00\
782\x06\x00\x01\x00\x00\x0e\x10\x00\x30\x04\x6e\x73\x32\x31\x07\x63\
783\x6c\x6f\x75\x64\x6e\x73\x03\x6e\x65\x74\x00\x07\x73\x75\x70\x70\
784\x6f\x72\x74\xc0\x2c\x78\x77\xe2\xf1\x00\x00\x1c\x20\x00\x00\x07\
785\x08\x00\x12\x75\x00\x00\x00\x0e\x10\xc0\x0c\x00\x2e\x00\x01\x00\
786\x00\x0e\x10\x00\x5d\x00\x06\x0d\x02\x00\x00\x0e\x10\x61\xf2\xaf\
787\x39\x61\xcb\x22\x39\x28\xeb\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\
788\x6b\x00\x2f\x79\xed\x73\xd0\x75\xc8\xa9\xa2\x2a\x08\x3c\x78\xb9\
789\xfb\x43\xc5\x8e\x62\x4c\xbc\x36\xeb\x22\x96\x45\x59\x36\x1f\x69\
790\x3a\x7a\x5e\x67\xd0\x54\x1e\xf0\xf2\x11\x1a\x72\x00\x56\x89\x26\
791\x79\xf4\x06\x1a\x0c\x59\x41\x32\x60\x68\x75\x05\x3d\x90\xed\x1e\
792\x0f\xfc\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\x00\x02\xc0\x27\
793\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\x00\x08\x05\x70\x6e\x73\
794\x32\x33\xc0\x2c\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\x00\x07\
795\x04\x6e\x73\x32\x33\xc0\x2c\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\
796\x10\x00\x08\x05\x70\x6e\x73\x32\x32\xc0\x2c\xc0\x0c\x00\x02\x00\
797\x01\x00\x00\x0e\x10\x00\x08\x05\x70\x6e\x73\x32\x34\xc0\x2c\xc0\
798\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\x00\x07\x04\x6e\x73\x32\x34\
799\xc0\x2c\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\x00\x08\x05\x70\
800\x6e\x73\x32\x31\xc0\x2c\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\
801\x00\x07\x04\x6e\x73\x32\x32\xc0\x2c\xc0\x0c\x00\x2e\x00\x01\x00\
802\x00\x0e\x10\x00\x5d\x00\x02\x0d\x02\x00\x00\x0e\x10\x61\xf2\xaf\
803\x39\x61\xcb\x22\x39\x28\xeb\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\
804\x6b\x00\xd1\xc5\x6c\xf1\xfb\xd0\x75\xf1\x38\x20\x28\x80\x4c\xe0\
805\x59\xa5\xa8\xab\x84\x79\xd8\x37\x48\xa7\xa5\x3f\x08\x9b\x4c\xca\
806\x40\x2b\xcb\x2c\xda\xcc\xc2\x18\xad\x07\x9e\xf8\x4e\x17\x8d\xb1\
807\x2b\x2d\xa2\xa6\x17\xdb\x55\x30\xbc\xa2\xb9\xa0\x01\x71\x01\xe5\
808\xdc\x4f\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xd9\x14\
809\x70\xd0\xc0\x0c\x00\x2e\x00\x01\x00\x00\x0e\x10\x00\x5d\x00\x01\
810\x0d\x02\x00\x00\x0e\x10\x61\xf2\xaf\x39\x61\xcb\x22\x39\x28\xeb\
811\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\xf8\x57\x68\xf0\xad\
812\x9e\xfb\x3a\x0f\x66\xbd\xcc\x48\xe7\x29\x0a\xf4\xd8\xf6\xbe\xbc\
813\x04\x76\x02\x27\x64\xf2\xc9\x42\x6d\x75\x54\x83\x0a\x11\xda\x0a\
814\x02\x6b\x8c\xf1\x65\xc4\x21\x44\xea\x89\x09\x01\xc8\xa1\xe2\x11\
815\x8f\xed\x67\x39\x69\x33\xdd\x97\x22\x1a\xd3\xc0\x0c\x00\x0f\x00\
816\x01\x00\x00\x0e\x10\x00\x11\x00\x0a\x04\x6d\x61\x69\x6c\x04\x6e\
817\x69\x78\x64\x03\x6f\x72\x67\x00\xc0\x0c\x00\x2e\x00\x01\x00\x00\
818\x0e\x10\x00\x5d\x00\x0f\x0d\x02\x00\x00\x0e\x10\x61\xf2\xaf\x39\
819\x61\xcb\x22\x39\x28\xeb\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\
820\x00\xd4\xdd\x07\xd9\xb6\xb2\xba\x57\xa9\x1d\x3b\xaa\x6c\x55\xc4\
821\x3d\x73\x79\xea\x74\xfe\xd7\x23\x0c\xb4\xab\x8f\x4b\x1f\xd9\x8a\
822\xb2\x4a\x5c\xad\x3e\x8e\x4a\x85\xbb\xbd\x75\xf2\x47\x2c\xa8\x89\
823\x21\x75\x89\xb1\x12\xc4\xd2\xf7\x40\x06\x52\x57\x83\x8a\xaa\x7b\
824\x75\xc0\x0c\x00\x10\x00\x01\x00\x00\x0e\x10\x00\x34\x33\x76\x3d\
825\x73\x70\x66\x31\x20\x2b\x6d\x78\x20\x2b\x61\x3a\x6d\x61\x69\x6c\
826\x2e\x6e\x69\x78\x64\x2e\x6f\x72\x67\x20\x69\x70\x34\x3a\x32\x31\
827\x37\x2e\x32\x30\x2e\x31\x31\x32\x2e\x32\x30\x38\x20\x2d\x61\x6c\
828\x6c\xc0\x0c\x00\x2e\x00\x01\x00\x00\x0e\x10\x00\x5d\x00\x10\x0d\
829\x02\x00\x00\x0e\x10\x61\xf2\xaf\x39\x61\xcb\x22\x39\x28\xeb\x06\
830\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\xd1\x6c\xd1\xf4\x3b\xe0\
831\x44\xba\xfe\xe9\xdb\x82\xbd\x89\x5f\xa1\x07\x72\xdd\x47\xad\x4e\
832\x91\xd5\xc3\xfe\x3e\x39\x74\xdb\x50\x50\x19\x6c\x3f\x6c\xb7\xa8\
833\x01\x03\x6a\xf5\xa7\xf3\x9b\xf7\x76\xd4\xff\xa3\xd5\x43\xfc\xec\
834\xa9\x89\x24\xf8\xd2\xb6\x76\xd4\x20\xbc\xc0\x0c\x00\x30\x00\x01\
835\x00\x00\x0e\x10\x00\x44\x01\x01\x03\x0d\xf3\x87\xe2\x7c\x2b\x82\
836\x40\x72\x7c\xfd\xc9\x2b\xe8\x22\xd6\xa9\x40\xc0\xab\x03\x25\x7d\
837\x92\xae\xf3\x17\x71\x82\x67\xc6\xcd\xb6\x4b\x11\x62\xc6\xfa\x06\
838\xec\x4c\x9f\xd9\xe6\xaf\x5c\x3d\xe4\x32\xde\x11\x1b\x09\x13\xe3\
839\xd0\xba\x66\xd1\xbc\x32\xdb\x13\xd7\x1d\xc0\x0c\x00\x30\x00\x01\
840\x00\x00\x0e\x10\x00\x44\x01\x00\x03\x0d\xd4\x43\xde\x96\xe5\xea\
841\x0a\xf9\x5d\x4f\x72\x88\x9c\xd9\x9c\xf7\xa6\x3f\x12\xd7\xf3\xea\
842\x8a\x6b\x44\x4c\x79\x23\x81\x94\x43\xa3\xbd\x9e\xb8\xde\xfe\x8c\
843\xe6\x21\xe3\x8a\x71\xba\x05\xd2\x0f\x98\x5b\xfc\x7e\x72\x8c\xe9\
844\x9a\xc0\x49\x00\xca\xd5\x62\x93\x7f\x03\xc0\x0c\x00\x2e\x00\x01\
845\x00\x00\x0e\x10\x00\x5d\x00\x30\x0d\x02\x00\x00\x0e\x10\x61\xf2\
846\xaf\x39\x61\xcb\x22\x39\x28\xeb\x06\x72\x65\x6c\x6b\x6f\x6d\x02\
847\x73\x6b\x00\xc0\x93\x23\x1d\xcb\x1e\x79\xfe\x7c\x40\x3e\xd4\x33\
848\x5f\xed\x69\x8e\x7d\x75\xff\x73\x6b\x24\x71\x8f\x50\xf8\xe0\x49\
849\xce\x5f\x62\x0c\x8c\xb3\x06\x8f\x26\xea\x20\xa0\xe3\x71\xe0\xa1\
850\x8b\xe0\x4a\x2f\x1d\x4b\x79\x2c\x52\x6b\xa4\x43\xb5\x70\x27\x01\
851\xb0\x63\x47\xc0\x0c\x00\x2e\x00\x01\x00\x00\x0e\x10\x00\x5d\x00\
852\x30\x0d\x02\x00\x00\x0e\x10\x61\xf2\xaf\x39\x61\xcb\x22\x39\x97\
853\x18\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\x41\x87\x75\x1d\
854\x30\x44\xd1\x94\x40\xd4\xe6\x40\x98\x62\x94\x53\xad\x53\xe2\xed\
855\xc0\xc0\xb7\xa3\x20\x15\xae\x59\xbb\x97\x45\xfb\x0e\xbf\x70\xf3\
856\xb1\x24\x79\xe8\x85\x6c\x2a\x66\x10\xb6\x75\x99\x7b\x77\x78\x65\
857\xa6\x67\x8d\x59\xa6\x14\xf7\xe6\x77\xab\x53\x9c\xc0\x0c\x00\x33\
858\x00\x01\x00\x00\x02\x58\x00\x0d\x01\x00\x00\x0a\x08\x90\xc7\xf1\
859\x74\x0b\x0c\xfb\x34\xc0\x0c\x00\x2e\x00\x01\x00\x00\x02\x58\x00\
860\x5d\x00\x33\x0d\x02\x00\x00\x00\x00\x61\xf2\xaf\x39\x61\xcb\x22\
861\x39\x28\xeb\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\xc4\x4d\
862\x00\x48\x9c\x86\x49\xac\x8d\x03\x28\x23\xac\xec\xf5\x5b\xb6\xe5\
863\x2f\xf6\xae\xaa\x01\x5a\x66\x52\xf7\x43\xc3\xb1\xe5\xef\xe5\xbf\
864\x5f\x71\x5d\xa1\x57\x64\x66\x5e\xa1\x6f\x96\xa8\xcd\x48\x85\xe4\
865\x20\xe2\xfb\xb0\xc1\x00\x47\x72\xc8\x72\x98\xc7\x41\xd9";
866    
867    let dummy = 
868        DnsRequestHeader
869        {
870            header: DnsHeader {id: 0xd079, ..Default::default()},
871            payload: DnsRequestPayload
872                {
873                    qname: b"\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00".to_vec(),
874                    qtype: QType::ALL,
875                    qclass: QClass::IN, 
876                }
877        };
878
879    let now = Instant::now();
880    let ans = DnsRequestAnswer::try_from(pkt.as_slice());
881    let elapsed = now.elapsed();
882    println!("Elapsed: {:.2?}", elapsed);
883
884    assert_eq!(ans.is_ok(), true, "{}", ans.err().unwrap());
885    let ans = ans.unwrap();
886
887    let verif = ans.verify(&dummy);
888    assert_eq!(verif.is_ok(), true, "{}", verif.err().unwrap());
889
890    assert_eq!(ans.header.id, 0xd079);
891    assert_eq!(ans.header.ancount, 23);
892    assert_eq!(ans.header.arcount, 0);
893    assert_eq!(ans.header.nscount, 0);
894    assert_eq!(ans.header.qdcount, 1);
895
896    let ansord = &ans.response[0];
897    assert_eq!(
898        ansord.rdata, 
899        DnsRdata::SOA
900        { 
901            soa:
902                DnsSoa
903                {
904                    pnm: "ns21.cloudns.net".to_string(), 
905                    ram: "support.cloudns.net".to_string(), 
906                    serial: 2021122801,  
907                    interv_refr: 7200,
908                    interv_retry: 1800,
909                    expire_limit: 1209600,
910                    min_ttl: 3600
911                }
912        }
913    );
914    assert_eq!(ansord.name, "relkom.sk".to_string());
915    assert_eq!(ansord.dtype, QType::SOA);
916    assert_eq!(ansord.class, QClass::IN);
917    assert_eq!(ansord.ttl, 3600);
918    assert_eq!(ansord.rdlength, 48);
919
920    let a1 = 
921b"\x00\x06\x0d\x02\
922\x00\x00\x0e\x10\x61\xf2\xaf\x39\x61\xcb\x22\x39\x28\xeb\x06\x72\
923\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\x2f\x79\xed\x73\xd0\x75\xc8\
924\xa9\xa2\x2a\x08\x3c\x78\xb9\xfb\x43\xc5\x8e\x62\x4c\xbc\x36\xeb\
925\x22\x96\x45\x59\x36\x1f\x69\x3a\x7a\x5e\x67\xd0\x54\x1e\xf0\xf2\
926\x11\x1a\x72\x00\x56\x89\x26\x79\xf4\x06\x1a\x0c\x59\x41\x32\x60\
927\x68\x75\x05\x3d\x90\xed\x1e\x0f\xfc";
928
929    let ansord = &ans.response[1];
930    assert_eq!(ansord.rdata, DnsRdata::UNKNOWN{ data: a1.to_vec() });
931    assert_eq!(ansord.name, "relkom.sk".to_string());
932    assert_eq!(ansord.dtype, QType::RRSIG);
933    assert_eq!(ansord.class, QClass::IN);
934    assert_eq!(ansord.ttl, 3600);
935    assert_eq!(ansord.rdlength, 93);
936
937    let ansord = &ans.response[2];
938    assert_eq!(ansord.rdata, DnsRdata::NS{ fqdn: "ns21.cloudns.net".to_string() });
939    assert_eq!(ansord.name, "relkom.sk".to_string());
940    assert_eq!(ansord.dtype, QType::NS);
941    assert_eq!(ansord.class, QClass::IN);
942    assert_eq!(ansord.ttl, 3600);
943    assert_eq!(ansord.rdlength, 2);
944}
945
946
947#[test]
948fn test_request()
949{
950    use std::time::Instant;
951    use std::net::IpAddr;
952
953    let ipp = IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8));
954    let test = QDnsName::from(&ipp);
955
956
957    let now = Instant::now();
958
959    let req = DnsRequestHeader::construct_lookup(test, QType::PTR);
960
961    let elapsed = now.elapsed();
962    println!("Elapsed: {:.2?}", elapsed);
963
964    assert_eq!(req.is_ok(), true, "{}", req.err().unwrap());
965
966    let pkt = req.unwrap().to_bytes();
967    assert_eq!(pkt.is_ok(), true);
968    let pkt = pkt.unwrap();
969
970    let ctrl = 
971    b"\x15\xc8\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x38\x01\x38\
972    \x01\x38\x01\x38\x07\x69\x6e\x2d\x61\x64\x64\x72\x04\x61\x72\x70\
973    \x61\x00\x00\x0c\x00\x01";
974
975    assert_eq!(&pkt[2..], &ctrl[2..]);
976}
977
978#[test]
979fn test_request_1()
980{
981    use std::time::Instant;
982    use std::net::IpAddr;
983
984    let ipp = IpAddr::V4("100.150.111.80".parse().unwrap());
985    let test = QDnsName::from(&ipp);
986
987    let now = Instant::now();
988
989    let req = DnsRequestHeader::construct_lookup(test, QType::PTR);
990
991    let elapsed = now.elapsed();
992    println!("Elapsed: {:.2?}", elapsed);
993
994    assert_eq!(req.is_ok(), true, "{}", req.err().unwrap());
995
996    let pkt = req.unwrap().to_bytes();
997    assert_eq!(pkt.is_ok(), true);
998    let pkt = pkt.unwrap();
999
1000    let ctrl = 
1001    b"\x74\xa1\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x02\x38\x30\x03\
1002    \x31\x31\x31\x03\x31\x35\x30\x03\x31\x30\x30\x07\x69\x6e\x2d\x61\
1003    \x64\x64\x72\x04\x61\x72\x70\x61\x00\x00\x0c\x00\x01";
1004    
1005    assert_eq!(&pkt[2..], &ctrl[2..]);
1006}
1007
1008#[test]
1009fn test_request6()
1010{
1011    use std::time::Instant;
1012    use std::net::IpAddr;
1013    
1014    let ipp =  IpAddr::V6("2a00:1450:4003:802::200e".parse().unwrap());
1015    let test = QDnsName::from(&ipp);
1016
1017    let now = Instant::now();
1018
1019    let req = DnsRequestHeader::construct_lookup(test, QType::PTR);
1020
1021    let elapsed = now.elapsed();
1022    println!("Elapsed: {:.2?}", elapsed);
1023
1024    assert_eq!(req.is_ok(), true, "{}", req.err().unwrap());
1025
1026    let pkt = req.unwrap().to_bytes();
1027    assert_eq!(pkt.is_ok(), true);
1028    let pkt = pkt.unwrap();
1029
1030    let ctrl = 
1031    b"\xee\xec\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x65\x01\x30\
1032    \x01\x30\x01\x32\x01\x30\x01\x30\x01\x30\x01\x30\x01\x30\x01\x30\
1033    \x01\x30\x01\x30\x01\x30\x01\x30\x01\x30\x01\x30\x01\x32\x01\x30\
1034    \x01\x38\x01\x30\x01\x33\x01\x30\x01\x30\x01\x34\x01\x30\x01\x35\
1035    \x01\x34\x01\x31\x01\x30\x01\x30\x01\x61\x01\x32\x03\x69\x70\x36\
1036    \x04\x61\x72\x70\x61\x00\x00\x0c\x00\x01";
1037
1038    assert_eq!(&pkt[2..], &ctrl[2..]);
1039}
1040