scalpel/layers/
dns.rs

1//! Handling of DNS Packets.
2
3use core::convert::TryInto;
4use core::fmt;
5
6use serde::{Serialize, Serializer};
7
8use crate::errors::Error;
9use crate::layers::udp;
10use crate::types::{IPv4Address, IPv6Address};
11use crate::Layer;
12
13#[derive(Default, Debug, Serialize)]
14pub struct DNSSOA {
15    mname: DNSName,
16    rname: DNSName,
17    serial: u32,
18    refresh: u32,
19    retry: u32,
20    expire: u32,
21    minimum: u32,
22}
23
24#[derive(Debug, Serialize)]
25pub enum DNSRecordData {
26    Empty,
27    A(IPv4Address),
28    AAAA(IPv6Address),
29    CNAME(DNSName),
30    MB(DNSName),
31    MD(DNSName),
32    MF(DNSName),
33    MG(DNSName),
34    MINFO((DNSName, DNSName)),
35    MR(DNSName),
36    MX((u16, DNSName)),
37    PTR(DNSName),
38    NULL(Vec<u8>),
39    SOA(DNSSOA),
40}
41
42// Register ourselves with parent
43pub(crate) fn register_defaults() -> Result<(), Error> {
44    udp::register_app(53, DNS::creator)
45}
46
47#[derive(Default)]
48pub struct DNSName(Vec<u8>);
49
50impl fmt::Display for DNSName {
51    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52        let mut i = 0_usize;
53        let mut name_parts: Vec<&str> = Vec::new();
54        while self.0[i] != 0 {
55            let x = self.0[i] as usize;
56            let out_str = core::str::from_utf8(&self.0[i + 1..=i + x]);
57            if let Ok(out_str) = out_str {
58                name_parts.push(out_str);
59                i += x + 1;
60            }
61        }
62        let name = name_parts.join(".");
63        write!(f, "{}", name)
64    }
65}
66
67impl fmt::Debug for DNSName {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        fmt::Display::fmt(self, f)
70    }
71}
72
73impl Serialize for DNSName {
74    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75    where
76        S: Serializer,
77    {
78        serializer.serialize_str(format!("{}", self).as_str())
79    }
80}
81
82#[derive(Debug, Default, Serialize)]
83pub struct DNSQRecord {
84    name: DNSName,
85    #[serde(rename = "type")]
86    type_: u16,
87    class: u16,
88}
89
90#[derive(Debug, Serialize)]
91pub struct DNSResRecord {
92    name: DNSName,
93    #[serde(rename = "type")]
94    type_: u16,
95    class: u16,
96    ttl: u32,
97    rdlength: u16,
98    #[serde(flatten)]
99    rdata: DNSRecordData,
100}
101
102#[derive(Debug, Default, Serialize)]
103pub struct DNS {
104    #[serde(serialize_with = "crate::types::hex::serialize_lower_hex_u16")]
105    id: u16,
106    qr: bool,
107    opcode: u8,
108    aa: bool,
109    tc: bool,
110    rd: bool,
111    ra: bool,
112    z: u8,
113    rcode: u8,
114    qdcount: u16,
115    ancount: u16,
116    nscount: u16,
117    arcount: u16,
118    questions: Vec<DNSQRecord>,
119    answers: Vec<DNSResRecord>,
120    nameservers: Vec<DNSResRecord>,
121    additional: Vec<DNSResRecord>,
122}
123
124impl DNS {
125    pub(crate) fn creator() -> Box<dyn Layer + Send> {
126        Box::<DNS>::default()
127    }
128
129    // A Name needs to be dissected recursively by looking at previous occurence of a name
130    // See https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.4
131    fn dns_name_from_bytes(
132        bytes: &[u8],
133        start: usize,
134        remaining: usize,
135    ) -> Result<(DNSName, usize), Error> {
136        // Get labels for the name. This can get called recursively.
137        fn labels_from_offset(
138            bytes: &[u8],
139            offset: usize,
140            labels: &mut Vec<u8>,
141            mut remaining: usize,
142            check_remaining: bool,
143        ) -> Result<usize, Error> {
144            let mut i = offset;
145
146            if offset > bytes.len() {
147                return Err(Error::ParseError(format!(
148                    "Offset is : {}, remaining : {}",
149                    offset,
150                    bytes.len(),
151                )));
152            }
153
154            let mut consumed = 0;
155            let _ = loop {
156                let ptr = bytes[i] & 0xC0;
157                match ptr {
158                    0xC0 => {
159                        // This is in offset form, collect labels
160                        // 1. The offset is from the start of DNS layer, but the slice we are
161                        //    dealing with is past the first header (12 bytes), hence we subtract
162                        //    the offset.
163                        //  2. The `labels` param 'collects' the labels and we only consume two
164                        //     bytes.
165                        let previous =
166                            ((bytes[i] & 0x3f) as u16) << 8 | ((bytes[i + 1] as u16) - 12);
167                        let _ = labels_from_offset(bytes, previous as usize, labels, 0, false)?;
168                        consumed += 2;
169                        break true;
170                    }
171                    0x00 => {
172                        if bytes[i] == 0x00 {
173                            consumed += 1;
174                            labels.push(bytes[i]);
175                            break false;
176                        }
177
178                        // Collect a single label
179                        let count = bytes[i] as usize + 1_usize;
180                        if check_remaining {
181                            if remaining < count {
182                                return Err(Error::TooShort {
183                                    required: count,
184                                    available: remaining,
185                                    data: hex::encode(&bytes[i..]),
186                                });
187                            }
188                            consumed += count;
189                            remaining -= count;
190                        }
191
192                        labels.extend_from_slice(&bytes[i..i + count]);
193                        i += count;
194                    }
195                    _ => {
196                        return Err(Error::ParseError(format!("Unknown DNS PTR: {:x}", ptr)));
197                    }
198                }
199            };
200
201            Ok(consumed)
202        }
203
204        // Note: 24 seems to be the 'sweetest' spot in terms of reserved capacity.
205        let mut labels = Vec::with_capacity(24);
206        let consumed = labels_from_offset(bytes, start, &mut labels, remaining, true)?;
207
208        Ok((DNSName(labels), consumed))
209    }
210
211    fn dns_resrecord_from_bytes(
212        bytes: &[u8],
213        start: usize,
214        mut remaining: usize,
215    ) -> Result<(DNSResRecord, usize), Error> {
216        let mut i = 0;
217        let mut offset = start;
218        let (name, consumed) = Self::dns_name_from_bytes(bytes, start, remaining)?;
219
220        i += consumed;
221        remaining -= consumed;
222        offset += consumed;
223
224        if remaining < 10 {
225            return Err(Error::TooShort {
226                required: 10,
227                available: remaining,
228                data: hex::encode(&bytes[i..]),
229            });
230        }
231
232        let type_ = (bytes[offset] as u16) << 8 | (bytes[offset + 1] as u16);
233        let class = (bytes[offset + 2] as u16) << 8 | (bytes[offset + 3] as u16);
234        let ttl = (bytes[offset + 4] as u32) << 24
235            | (bytes[offset + 5] as u32) << 16
236            | (bytes[offset + 6] as u32) << 8
237            | (bytes[offset + 7] as u32);
238        let rdlength = (bytes[offset + 8] as u16) << 8 | (bytes[offset + 9] as u16);
239        offset += 10;
240        if remaining < (rdlength as usize) {
241            return Err(Error::TooShort {
242                required: rdlength as usize,
243                available: remaining,
244                data: hex::encode(&bytes[offset..]),
245            });
246        }
247        let rdata_buffer = &bytes[offset..offset + rdlength as usize];
248
249        i += 10;
250        remaining -= 10;
251
252        let rdata = match type_ {
253            1 => DNSRecordData::A(rdata_buffer.try_into().unwrap()), /* A */
254            28 => DNSRecordData::AAAA(rdata_buffer.try_into().unwrap()), /* AAAA */
255            2 | 3 | 4 | 5 | 7 | 8 | 9 => {
256                let (name, _) = Self::dns_name_from_bytes(bytes, offset, remaining)?;
257                DNSRecordData::CNAME(name)
258            }
259            6 =>
260            /* SOA */
261            {
262                // FIXME: into an inline function?
263                let mut offset = offset;
264                let mut remaining = remaining;
265                let (mname, consumed) = Self::dns_name_from_bytes(bytes, offset, remaining)?;
266                offset += consumed;
267                remaining -= consumed;
268                let (rname, consumed) = Self::dns_name_from_bytes(bytes, offset, remaining)?;
269                offset += consumed;
270                remaining -= consumed;
271                if remaining < 20 {
272                    return Err(Error::TooShort {
273                        required: 20,
274                        available: remaining,
275                        data: hex::encode(&bytes[offset..]),
276                    });
277                }
278                // serial, refresh, retry, expire, minimum
279                let serial = (bytes[offset] as u32) << 24
280                    | (bytes[offset + 1] as u32) << 16
281                    | (bytes[offset + 2] as u32) << 8
282                    | (bytes[offset + 3] as u32);
283                let refresh = (bytes[offset + 4] as u32) << 24
284                    | (bytes[offset + 5] as u32) << 16
285                    | (bytes[offset + 6] as u32) << 8
286                    | (bytes[offset + 7] as u32);
287                let retry = (bytes[offset + 8] as u32) << 24
288                    | (bytes[offset + 9] as u32) << 16
289                    | (bytes[offset + 10] as u32) << 8
290                    | (bytes[offset + 11] as u32);
291                let expire = (bytes[offset + 12] as u32) << 24
292                    | (bytes[offset + 13] as u32) << 16
293                    | (bytes[offset + 14] as u32) << 8
294                    | (bytes[offset + 15] as u32);
295                let minimum = (bytes[offset + 16] as u32) << 24
296                    | (bytes[offset + 17] as u32) << 16
297                    | (bytes[offset + 18] as u32) << 8
298                    | (bytes[offset + 19] as u32);
299
300                // offset += 20;
301                // remaining -= 20;
302
303                DNSRecordData::SOA(DNSSOA {
304                    mname,
305                    rname,
306                    serial,
307                    refresh,
308                    retry,
309                    expire,
310                    minimum,
311                })
312            }
313            // 41 => unimplemented!("Parse OPT"),
314            _ => DNSRecordData::NULL(rdata_buffer.into()),
315        };
316        i += rdlength as usize;
317        // remaining -= rdlength as usize;
318
319        Ok((
320            DNSResRecord {
321                name,
322                type_,
323                class,
324                ttl,
325                rdlength,
326                rdata,
327            },
328            i,
329        ))
330    }
331
332    fn records_from_bytes(&mut self, bytes: &[u8], mut remaining: usize) -> Result<usize, Error> {
333        let mut i = 0;
334
335        // First questions
336        for _ in 0..self.qdcount {
337            let (name, consumed) = Self::dns_name_from_bytes(bytes, i, remaining)?;
338
339            i += consumed;
340            remaining -= consumed;
341
342            let type_ = (bytes[i] as u16) << 8 | (bytes[i + 1] as u16);
343            let class = (bytes[i + 2] as u16) << 8 | (bytes[i + 3] as u16);
344
345            self.questions.push(DNSQRecord { name, type_, class });
346            i += 4;
347            remaining -= 4;
348        }
349
350        for _ in 0..self.ancount {
351            let (record, consumed) = Self::dns_resrecord_from_bytes(bytes, i, remaining)?;
352
353            i += consumed;
354            remaining -= consumed;
355            self.answers.push(record);
356        }
357
358        for _ in 0..self.nscount {
359            let (record, consumed) = Self::dns_resrecord_from_bytes(bytes, i, remaining)?;
360
361            i += consumed;
362            remaining -= consumed;
363            self.nameservers.push(record);
364        }
365
366        for _ in 0..self.arcount {
367            let (record, consumed) = Self::dns_resrecord_from_bytes(bytes, i, remaining)?;
368
369            i += consumed;
370            remaining -= consumed;
371            self.additional.push(record);
372        }
373
374        Ok(i)
375    }
376}
377
378impl Layer for DNS {
379    fn decode_bytes(
380        &mut self,
381        bytes: &[u8],
382    ) -> Result<(Option<Box<dyn Layer + Send>>, usize), Error> {
383        let mut decoded;
384
385        if bytes.len() < 12 {
386            return Err(Error::TooShort {
387                required: 12,
388                available: bytes.len(),
389                data: hex::encode(bytes),
390            });
391        }
392
393        //self.id = U16Hex((bytes[0] as u16) << 8 | (bytes[1] as u16));
394        self.id = (bytes[0] as u16) << 8 | (bytes[1] as u16);
395
396        let first = bytes[2];
397        self.qr = (first & 0x80) != 0x00;
398        self.opcode = (first & 0x78) >> 3;
399        self.aa = (first & 0x04) != 0x00;
400        self.tc = (first & 0x02) != 0x00;
401        self.rd = (first & 0x01) != 0x00;
402
403        let second = bytes[3];
404        self.ra = (second & 0x80) != 0x80;
405        self.z = 0;
406        self.rcode = second & 0x0f;
407
408        self.qdcount = (bytes[4] as u16) << 8 | (bytes[5] as u16);
409        self.ancount = (bytes[6] as u16) << 8 | (bytes[7] as u16);
410        self.nscount = (bytes[8] as u16) << 8 | (bytes[9] as u16);
411        self.arcount = (bytes[10] as u16) << 8 | (bytes[11] as u16);
412
413        decoded = 12;
414
415        let remaining = bytes.len() - 12;
416        let record_bytes = self.records_from_bytes(&bytes[decoded..], remaining)?;
417        decoded += record_bytes;
418
419        Ok((None, decoded))
420    }
421
422    fn name(&self) -> &'static str {
423        "DNS"
424    }
425
426    fn short_name(&self) -> &'static str {
427        "dns"
428    }
429}
430
431#[cfg(test)]
432mod tests {
433
434    use crate::layers;
435    use crate::{Layer, Packet, ENCAP_TYPE_ETH};
436
437    wasm_tests! {
438        #[test]
439        fn parse_valid_dns_packet() {
440            let _ = layers::register_defaults();
441
442            let dns_query = vec![
443                0x52, 0x54, 0x00, 0xbd, 0x1c, 0x70, 0xfe, 0x54, /* RT...p.T */
444                0x00, 0x3e, 0x00, 0x96, 0x08, 0x00, 0x45, 0x00, /* .>....E. */
445                0x00, 0xe0, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, /* ....@.@. */
446                0xc4, 0x74, 0xc0, 0xa8, 0x7a, 0x01, 0xc0, 0xa8, /* .t..z... */
447                0x7a, 0x46, 0x00, 0x35, 0xdb, 0x13, 0x00, 0xcc, /* zF.5.... */
448                0x76, 0x76, /* DNS */ 0xf3, 0x03, 0x81, 0x80, 0x00, 0x01, /* vv...... */
449                0x00, 0x01, 0x00, 0x04, 0x00, 0x04, 0x03, 0x77, /* .......w */
450                0x77, 0x77, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, /* ww.googl */
451                0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x1c, /* e.com... */
452                0x00, 0x01, 0xc0, 0x0c, 0x00, 0x1c, 0x00, 0x01, /* ........ */
453                0x00, 0x00, 0x01, 0x2c, 0x00, 0x10, 0x2a, 0x00, /* ...,..*. */
454                0x14, 0x50, 0x40, 0x0c, 0x0c, 0x01, 0x00, 0x00, /* .P@..... */
455                0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xc0, 0x10, /* .....i.. */
456                0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, /* ........ */
457                0x00, 0x06, 0x03, 0x6e, 0x73, 0x34, 0xc0, 0x10, /* ...ns4.. */
458                0xc0, 0x10, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, /* ........ */
459                0xa3, 0x00, 0x00, 0x06, 0x03, 0x6e, 0x73, 0x32, /* .....ns2 */
460                0xc0, 0x10, 0xc0, 0x10, 0x00, 0x02, 0x00, 0x01, /* ........ */
461                0x00, 0x02, 0xa3, 0x00, 0x00, 0x06, 0x03, 0x6e, /* .......n */
462                0x73, 0x31, 0xc0, 0x10, 0xc0, 0x10, 0x00, 0x02, /* s1...... */
463                0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, 0x00, 0x06, /* ........ */
464                0x03, 0x6e, 0x73, 0x33, 0xc0, 0x10, 0xc0, 0x6c, /* .ns3...l */
465                0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, /* ........ */
466                0x00, 0x04, 0xd8, 0xef, 0x20, 0x0a, 0xc0, 0x5a, /* .... ..Z */
467                0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, /* ........ */
468                0x00, 0x04, 0xd8, 0xef, 0x22, 0x0a, 0xc0, 0x7e, /* ...."..~ */
469                0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, /* ........ */
470                0x00, 0x04, 0xd8, 0xef, 0x24, 0x0a, 0xc0, 0x48, /* ....$..H */
471                0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, /* ........ */
472                0x00, 0x04, 0xd8, 0xef, 0x26, 0x0a, /* ....&. */
473            ];
474
475            let mut dns: Box<dyn Layer> = Box::new(super::DNS::default());
476
477            let p = dns.decode_bytes(&dns_query[42..]);
478            assert!(p.is_ok(), "{:#?}", dns);
479        }
480
481        #[test]
482        fn test_dns_parse_gopacket_regression() {
483            let _ = layers::register_defaults();
484
485            // testPacketDNSRegression is the packet:
486            //   11:08:05.708342 IP 109.194.160.4.57766 > 95.211.92.14.53: 63000% [1au] A? picslife.ru. (40)
487            //      0x0000:  0022 19b6 7e22 000f 35bb 0b40 0800 4500  ."..~"..5..@..E.
488            //      0x0010:  0044 89c4 0000 3811 2f3d 6dc2 a004 5fd3  .D....8./=m..._.
489            //      0x0020:  5c0e e1a6 0035 0030 a597 f618 0010 0001  \....5.0........
490            //      0x0030:  0000 0000 0001 0870 6963 736c 6966 6502  .......picslife.
491            //      0x0040:  7275 0000 0100 0100 0029 1000 0000 8000  ru.......)......
492            //      0x0050:  0000                                     ..
493            let test_packet_dns_regression = vec![
494                0x00, 0x22, 0x19, 0xb6, 0x7e, 0x22, 0x00, 0x0f, 0x35, 0xbb, 0x0b, 0x40, 0x08, 0x00,
495                0x45, 0x00, 0x00, 0x44, 0x89, 0xc4, 0x00, 0x00, 0x38, 0x11, 0x2f, 0x3d, 0x6d, 0xc2,
496                0xa0, 0x04, 0x5f, 0xd3, 0x5c, 0x0e, 0xe1, 0xa6, 0x00, 0x35, 0x00, 0x30, 0xa5, 0x97,
497                0xf6, 0x18, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x70,
498                0x69, 0x63, 0x73, 0x6c, 0x69, 0x66, 0x65, 0x02, 0x72, 0x75, 0x00, 0x00, 0x01, 0x00,
499                0x01, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
500            ];
501            let p = Packet::from_bytes(&test_packet_dns_regression, ENCAP_TYPE_ETH);
502            assert!(p.is_ok());
503            let p = p.unwrap();
504            assert!(p.layers.len() == 4, "{:#?}", p);
505        }
506    }
507}