dns_parser_joe/
name.rs

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