wez_mdns/dns_parser/
header.rs

1use crate::dns_parser::{Error, Opcode, ResponseCode};
2use byteorder::{BigEndian, ByteOrder};
3
4mod flag {
5    pub const QUERY: u16 = 0b1000_0000_0000_0000;
6    pub const OPCODE_MASK: u16 = 0b0111_1000_0000_0000;
7    pub const AUTHORITATIVE: u16 = 0b0000_0100_0000_0000;
8    pub const TRUNCATED: u16 = 0b0000_0010_0000_0000;
9    pub const RECURSION_DESIRED: u16 = 0b0000_0001_0000_0000;
10    pub const RECURSION_AVAILABLE: u16 = 0b0000_0000_1000_0000;
11    pub const AUTHENTICATED_DATA: u16 = 0b0000_0000_0010_0000;
12    pub const CHECKING_DISABLED: u16 = 0b0000_0000_0001_0000;
13    pub const RESERVED_MASK: u16 = 0b0000_0000_0100_0000;
14    pub const RESPONSE_CODE_MASK: u16 = 0b0000_0000_0000_1111;
15}
16
17/// Represents parsed header of the packet
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
19#[allow(missing_docs)] // fields are from the spec I think
20pub struct Header {
21    pub id: u16,
22    pub query: bool,
23    pub opcode: Opcode,
24    pub authoritative: bool,
25    pub truncated: bool,
26    pub recursion_desired: bool,
27    pub recursion_available: bool,
28    pub authenticated_data: bool,
29    pub checking_disabled: bool,
30    pub response_code: ResponseCode,
31    pub questions: u16,
32    pub answers: u16,
33    pub nameservers: u16,
34    pub additional: u16,
35}
36
37impl Header {
38    /// Parse the header into a header structure
39    pub fn parse(data: &[u8]) -> Result<Header, Error> {
40        if data.len() < 12 {
41            return Err(Error::HeaderTooShort);
42        }
43        let flags = BigEndian::read_u16(&data[2..4]);
44        if flags & flag::RESERVED_MASK != 0 {
45            return Err(Error::ReservedBitsAreNonZero);
46        }
47        let header = Header {
48            id: BigEndian::read_u16(&data[..2]),
49            query: flags & flag::QUERY == 0,
50            opcode: ((flags & flag::OPCODE_MASK) >> flag::OPCODE_MASK.trailing_zeros()).into(),
51            authoritative: flags & flag::AUTHORITATIVE != 0,
52            truncated: flags & flag::TRUNCATED != 0,
53            recursion_desired: flags & flag::RECURSION_DESIRED != 0,
54            recursion_available: flags & flag::RECURSION_AVAILABLE != 0,
55            authenticated_data: flags & flag::AUTHENTICATED_DATA != 0,
56            checking_disabled: flags & flag::CHECKING_DISABLED != 0,
57            response_code: From::from((flags & flag::RESPONSE_CODE_MASK) as u8),
58            questions: BigEndian::read_u16(&data[4..6]),
59            answers: BigEndian::read_u16(&data[6..8]),
60            nameservers: BigEndian::read_u16(&data[8..10]),
61            additional: BigEndian::read_u16(&data[10..12]),
62        };
63        Ok(header)
64    }
65    /// Write a header to a buffer slice
66    ///
67    /// # Panics
68    ///
69    /// When buffer size is not exactly 12 bytes
70    pub fn write(&self, data: &mut [u8]) {
71        if data.len() != 12 {
72            panic!("Header size is exactly 12 bytes");
73        }
74        let mut flags = 0u16;
75        flags |= Into::<u16>::into(self.opcode) << flag::OPCODE_MASK.trailing_zeros();
76        flags |= Into::<u8>::into(self.response_code) as u16;
77        if !self.query {
78            flags |= flag::QUERY;
79        }
80        if self.authoritative {
81            flags |= flag::AUTHORITATIVE;
82        }
83        if self.recursion_desired {
84            flags |= flag::RECURSION_DESIRED;
85        }
86        if self.recursion_available {
87            flags |= flag::RECURSION_AVAILABLE;
88        }
89        if self.truncated {
90            flags |= flag::TRUNCATED;
91        }
92        BigEndian::write_u16(&mut data[..2], self.id);
93        BigEndian::write_u16(&mut data[2..4], flags);
94        BigEndian::write_u16(&mut data[4..6], self.questions);
95        BigEndian::write_u16(&mut data[6..8], self.answers);
96        BigEndian::write_u16(&mut data[8..10], self.nameservers);
97        BigEndian::write_u16(&mut data[10..12], self.additional);
98    }
99    /// Set "truncated flag" in the raw data
100    // shouldn't this method be non-public?
101    pub fn set_truncated(data: &mut [u8]) {
102        let oldflags = BigEndian::read_u16(&data[2..4]);
103        BigEndian::write_u16(&mut data[2..4], oldflags & flag::TRUNCATED);
104    }
105    /// Returns a size of the header (always 12 bytes)
106    pub fn size() -> usize {
107        12
108    }
109}
110
111#[cfg(test)]
112mod test {
113
114    use crate::dns_parser::Header;
115    use crate::dns_parser::Opcode::*;
116    use crate::dns_parser::ResponseCode::NoError;
117
118    #[test]
119    fn parse_example_query() {
120        let query = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
121                      \x07example\x03com\x00\x00\x01\x00\x01";
122        let header = Header::parse(query).unwrap();
123        assert_eq!(
124            header,
125            Header {
126                id: 1573,
127                query: true,
128                opcode: StandardQuery,
129                authoritative: false,
130                truncated: false,
131                recursion_desired: true,
132                recursion_available: false,
133                authenticated_data: false,
134                checking_disabled: false,
135                response_code: NoError,
136                questions: 1,
137                answers: 0,
138                nameservers: 0,
139                additional: 0,
140            }
141        );
142    }
143
144    #[test]
145    fn parse_example_response() {
146        let response = b"\x06%\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
147                         \x07example\x03com\x00\x00\x01\x00\x01\
148                         \xc0\x0c\x00\x01\x00\x01\x00\x00\x04\xf8\
149                         \x00\x04]\xb8\xd8\"";
150        let header = Header::parse(response).unwrap();
151        assert_eq!(
152            header,
153            Header {
154                id: 1573,
155                query: false,
156                opcode: StandardQuery,
157                authoritative: false,
158                truncated: false,
159                recursion_desired: true,
160                recursion_available: true,
161                authenticated_data: false,
162                checking_disabled: false,
163                response_code: NoError,
164                questions: 1,
165                answers: 1,
166                nameservers: 0,
167                additional: 0,
168            }
169        );
170    }
171
172    #[test]
173    fn parse_query_with_ad_set() {
174        let query = b"\x06%\x01\x20\x00\x01\x00\x00\x00\x00\x00\x00\
175                      \x07example\x03com\x00\x00\x01\x00\x01";
176        let header = Header::parse(query).unwrap();
177        assert_eq!(
178            header,
179            Header {
180                id: 1573,
181                query: true,
182                opcode: StandardQuery,
183                authoritative: false,
184                truncated: false,
185                recursion_desired: true,
186                recursion_available: false,
187                authenticated_data: true,
188                checking_disabled: false,
189                response_code: NoError,
190                questions: 1,
191                answers: 0,
192                nameservers: 0,
193                additional: 0,
194            }
195        );
196    }
197
198    #[test]
199    fn parse_query_with_cd_set() {
200        let query = b"\x06%\x01\x10\x00\x01\x00\x00\x00\x00\x00\x00\
201                      \x07example\x03com\x00\x00\x01\x00\x01";
202        let header = Header::parse(query).unwrap();
203        assert_eq!(
204            header,
205            Header {
206                id: 1573,
207                query: true,
208                opcode: StandardQuery,
209                authoritative: false,
210                truncated: false,
211                recursion_desired: true,
212                recursion_available: false,
213                authenticated_data: false,
214                checking_disabled: true,
215                response_code: NoError,
216                questions: 1,
217                answers: 0,
218                nameservers: 0,
219                additional: 0,
220            }
221        );
222    }
223}