dns_parser_revived/
name.rs

1use std::fmt;
2use std::fmt::Write;
3use std::str::from_utf8;
4
5// Deprecated since rustc 1.23
6#[allow(unused_imports, deprecated)]
7use std::ascii::AsciiExt;
8
9use byteorder::{BigEndian, ByteOrder};
10
11use crate::Error;
12
13/// The DNS name as stored in the original packet
14///
15/// This contains just a reference to a slice that contains the data.
16/// You may turn this into a string using `.to_string()`
17#[derive(Clone, Copy)]
18pub struct Name<'a> {
19    labels: &'a [u8],
20    /// This is the original buffer size. The compressed names in original
21    /// are calculated in this buffer
22    original: &'a [u8],
23}
24
25impl<'a> Name<'a> {
26    /// Scan the data to get Name object
27    ///
28    /// The `data` should be a part of `original` where name should start.
29    /// The `original` is the data starting a the start of a packet, so
30    /// that offsets in compressed name starts from the `original`.
31    pub fn scan(data: &'a [u8], original: &'a [u8]) -> Result<Name<'a>, Error> {
32        let mut parse_data = data;
33        let mut return_pos = None;
34        let mut pos = 0;
35        if parse_data.len() <= pos {
36            return Err(Error::UnexpectedEOF);
37        }
38        // By setting the largest_pos to be the original len, a side effect
39        // is that the pos variable can move forwards in the buffer once.
40        let mut largest_pos = original.len();
41        let mut byte = parse_data[pos];
42        while byte != 0 {
43            if parse_data.len() <= pos {
44                return Err(Error::UnexpectedEOF);
45            }
46            if byte & 0b1100_0000 == 0b1100_0000 {
47                if parse_data.len() < pos + 2 {
48                    return Err(Error::UnexpectedEOF);
49                }
50                let off = (BigEndian::read_u16(&parse_data[pos..pos + 2]) & !0b1100_0000_0000_0000)
51                    as usize;
52                if off >= original.len() {
53                    return Err(Error::UnexpectedEOF);
54                }
55                // Set value for return_pos which is the pos in the original
56                // data buffer that should be used to return after validating
57                // the offsetted labels.
58                if return_pos.is_none() {
59                    return_pos = Some(pos);
60                }
61
62                // Check then set largest_pos to ensure we never go backwards
63                // in the buffer.
64                if off >= largest_pos {
65                    return Err(Error::BadPointer);
66                }
67                largest_pos = off;
68                pos = 0;
69                parse_data = &original[off..];
70            } else if byte & 0b1100_0000 == 0 {
71                let end = pos + byte as usize + 1;
72                if parse_data.len() < end {
73                    return Err(Error::UnexpectedEOF);
74                }
75                if !parse_data[pos + 1..end].is_ascii() {
76                    return Err(Error::LabelIsNotAscii);
77                }
78                pos = end;
79                if parse_data.len() <= pos {
80                    return Err(Error::UnexpectedEOF);
81                }
82            } else {
83                return Err(Error::UnknownLabelFormat);
84            }
85            byte = parse_data[pos];
86        }
87        if let Some(return_pos) = return_pos {
88            Ok(Name {
89                labels: &data[..return_pos + 2],
90                original,
91            })
92        } else {
93            Ok(Name {
94                labels: &data[..pos + 1],
95                original,
96            })
97        }
98    }
99    /// Number of bytes serialized name occupies
100    pub fn byte_len(&self) -> usize {
101        self.labels.len()
102    }
103}
104
105impl fmt::Display for Name<'_> {
106    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
107        let data = self.labels;
108        let original = self.original;
109        let mut pos = 0;
110        loop {
111            let byte = data[pos];
112            if byte == 0 {
113                return Ok(());
114            } else if byte & 0b1100_0000 == 0b1100_0000 {
115                let off =
116                    (BigEndian::read_u16(&data[pos..pos + 2]) & !0b1100_0000_0000_0000) as usize;
117                if pos != 0 {
118                    fmt.write_char('.')?;
119                }
120                return fmt::Display::fmt(&Name::scan(&original[off..], original).unwrap(), fmt);
121            } else if byte & 0b1100_0000 == 0 {
122                if pos != 0 {
123                    fmt.write_char('.')?;
124                }
125                let end = pos + byte as usize + 1;
126                fmt.write_str(from_utf8(&data[pos + 1..end]).unwrap())?;
127                pos = end;
128                continue;
129            } else {
130                unreachable!();
131            }
132        }
133    }
134}
135impl fmt::Debug for Name<'_> {
136    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
137        fmt.debug_tuple("Name").field(&format!("{}", self)).finish()
138    }
139}
140
141#[cfg(test)]
142mod test {
143    use crate::Error;
144    use crate::Name;
145
146    #[test]
147    fn parse_badpointer_same_offset() {
148        // A buffer where an offset points to itself,
149        // which is a bad compression pointer.
150        let same_offset = vec![192, 2, 192, 2];
151        let is_match = matches!(
152            Name::scan(&same_offset, &same_offset),
153            Err(Error::BadPointer)
154        );
155
156        assert!(is_match);
157    }
158
159    #[test]
160    fn parse_badpointer_forward_offset() {
161        // A buffer where the offsets points back to each other which causes
162        // infinite recursion if never checked, a bad compression pointer.
163        let forwards_offset = vec![192, 2, 192, 4, 192, 2];
164        let is_match = matches!(
165            Name::scan(&forwards_offset, &forwards_offset),
166            Err(Error::BadPointer)
167        );
168
169        assert!(is_match);
170    }
171
172    #[test]
173    fn nested_names() {
174        // A buffer where an offset points to itself, a bad compression pointer.
175        let buf = b"\x02xx\x00\x02yy\xc0\x00\x02zz\xc0\x04";
176
177        assert_eq!(Name::scan(&buf[..], buf).unwrap().to_string(), "xx");
178        assert_eq!(Name::scan(&buf[..], buf).unwrap().labels, b"\x02xx\x00");
179        assert_eq!(Name::scan(&buf[4..], buf).unwrap().to_string(), "yy.xx");
180        assert_eq!(
181            Name::scan(&buf[4..], buf).unwrap().labels,
182            b"\x02yy\xc0\x00"
183        );
184        assert_eq!(Name::scan(&buf[9..], buf).unwrap().to_string(), "zz.yy.xx");
185        assert_eq!(
186            Name::scan(&buf[9..], buf).unwrap().labels,
187            b"\x02zz\xc0\x04"
188        );
189    }
190}