dns_parser_revived/
header.rs

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