dns_parser_joe/
header.rs

1use byteorder::{BigEndian, ByteOrder};
2
3use {Error, ResponseCode, Opcode};
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)]
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    pub fn parse(data: &[u8]) -> Result<Header, Error> {
39        if data.len() < 12 {
40            return Err(Error::HeaderTooShort);
41        }
42        let flags = BigEndian::read_u16(&data[2..4]);
43        if flags & flag::RESERVED_MASK != 0 {
44            return Err(Error::ReservedBitsAreNonZero);
45        }
46        let header = Header {
47            id: BigEndian::read_u16(&data[..2]),
48            query: flags & flag::QUERY == 0,
49            opcode: ((flags & flag::OPCODE_MASK)
50                     >> 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)
76            << flag::OPCODE_MASK.trailing_zeros();
77        flags |= Into::<u8>::into(self.response_code) as u16;
78        if !self.query { flags |= flag::QUERY; }
79        if self.authoritative { flags |= flag::AUTHORITATIVE; }
80        if self.recursion_desired { flags |= flag::RECURSION_DESIRED; }
81        if self.recursion_available { flags |= flag::RECURSION_AVAILABLE; }
82        if self.truncated { flags |= flag::TRUNCATED; }
83        BigEndian::write_u16(&mut data[..2], self.id);
84        BigEndian::write_u16(&mut data[2..4], flags);
85        BigEndian::write_u16(&mut data[4..6], self.questions);
86        BigEndian::write_u16(&mut data[6..8], self.answers);
87        BigEndian::write_u16(&mut data[8..10], self.nameservers);
88        BigEndian::write_u16(&mut data[10..12], self.additional);
89    }
90    pub fn set_truncated(data: &mut [u8]) {
91        let oldflags = BigEndian::read_u16(&data[2..4]);
92        BigEndian::write_u16(&mut data[2..4], oldflags & flag::TRUNCATED);
93    }
94    pub fn size() -> usize { 12 }
95}
96
97
98#[cfg(test)]
99mod test {
100
101    use {Header};
102    use Opcode::*;
103    use ResponseCode::NoError;
104
105    #[test]
106    fn parse_example_query() {
107        let query = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
108                      \x07example\x03com\x00\x00\x01\x00\x01";
109        let header = Header::parse(query).unwrap();
110        assert_eq!(header, Header {
111            id: 1573,
112            query: true,
113            opcode: StandardQuery,
114            authoritative: false,
115            truncated: false,
116            recursion_desired: true,
117            recursion_available: false,
118            authenticated_data: false,
119            checking_disabled: false,
120            response_code: NoError,
121            questions: 1,
122            answers: 0,
123            nameservers: 0,
124            additional: 0,
125        });
126    }
127
128    #[test]
129    fn parse_example_response() {
130        let response = b"\x06%\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
131                         \x07example\x03com\x00\x00\x01\x00\x01\
132                         \xc0\x0c\x00\x01\x00\x01\x00\x00\x04\xf8\
133                         \x00\x04]\xb8\xd8\"";
134        let header = Header::parse(response).unwrap();
135        assert_eq!(header, Header {
136            id: 1573,
137            query: false,
138            opcode: StandardQuery,
139            authoritative: false,
140            truncated: false,
141            recursion_desired: true,
142            recursion_available: true,
143            authenticated_data: false,
144            checking_disabled: false,
145            response_code: NoError,
146            questions: 1,
147            answers: 1,
148            nameservers: 0,
149            additional: 0,
150        });
151    }
152
153    #[test]
154    fn parse_query_with_ad_set() {
155        let query = b"\x06%\x01\x20\x00\x01\x00\x00\x00\x00\x00\x00\
156                      \x07example\x03com\x00\x00\x01\x00\x01";
157        let header = Header::parse(query).unwrap();
158        assert_eq!(header, Header {
159            id: 1573,
160            query: true,
161            opcode: StandardQuery,
162            authoritative: false,
163            truncated: false,
164            recursion_desired: true,
165            recursion_available: false,
166            authenticated_data: true,
167            checking_disabled: false,
168            response_code: NoError,
169            questions: 1,
170            answers: 0,
171            nameservers: 0,
172            additional: 0,
173        });
174    }
175
176    #[test]
177    fn parse_query_with_cd_set() {
178        let query = b"\x06%\x01\x10\x00\x01\x00\x00\x00\x00\x00\x00\
179                      \x07example\x03com\x00\x00\x01\x00\x01";
180        let header = Header::parse(query).unwrap();
181        assert_eq!(header, Header {
182            id: 1573,
183            query: true,
184            opcode: StandardQuery,
185            authoritative: false,
186            truncated: false,
187            recursion_desired: true,
188            recursion_available: false,
189            authenticated_data: false,
190            checking_disabled: true,
191            response_code: NoError,
192            questions: 1,
193            answers: 0,
194            nameservers: 0,
195            additional: 0,
196        });
197    }
198}