mio_httpc/dns_parser/
name.rs

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