dns_parser/
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)]
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)
52                     >> flag::OPCODE_MASK.trailing_zeros()).into(),
53            authoritative: flags & flag::AUTHORITATIVE != 0,
54            truncated: flags & flag::TRUNCATED != 0,
55            recursion_desired: flags & flag::RECURSION_DESIRED != 0,
56            recursion_available: flags & flag::RECURSION_AVAILABLE != 0,
57            authenticated_data: flags & flag::AUTHENTICATED_DATA != 0,
58            checking_disabled: flags & flag::CHECKING_DISABLED != 0,
59            response_code: From::from((flags&flag::RESPONSE_CODE_MASK) as u8),
60            questions: BigEndian::read_u16(&data[4..6]),
61            answers: BigEndian::read_u16(&data[6..8]),
62            nameservers: BigEndian::read_u16(&data[8..10]),
63            additional: BigEndian::read_u16(&data[10..12]),
64        };
65        Ok(header)
66    }
67    /// Write a header to a buffer slice
68    ///
69    /// # Panics
70    ///
71    /// When buffer size is not exactly 12 bytes
72    pub fn write(&self, data: &mut [u8]) {
73        if data.len() != 12 {
74            panic!("Header size is exactly 12 bytes");
75        }
76        let mut flags = 0u16;
77        flags |= Into::<u16>::into(self.opcode)
78            << flag::OPCODE_MASK.trailing_zeros();
79        flags |= Into::<u8>::into(self.response_code) as u16;
80        if !self.query { flags |= flag::QUERY; }
81        if self.authoritative { flags |= flag::AUTHORITATIVE; }
82        if self.recursion_desired { flags |= flag::RECURSION_DESIRED; }
83        if self.recursion_available { flags |= flag::RECURSION_AVAILABLE; }
84        if self.truncated { flags |= flag::TRUNCATED; }
85        BigEndian::write_u16(&mut data[..2], self.id);
86        BigEndian::write_u16(&mut data[2..4], flags);
87        BigEndian::write_u16(&mut data[4..6], self.questions);
88        BigEndian::write_u16(&mut data[6..8], self.answers);
89        BigEndian::write_u16(&mut data[8..10], self.nameservers);
90        BigEndian::write_u16(&mut data[10..12], self.additional);
91    }
92    /// Set "truncated flag" in the raw data
93    // shouldn't this method be non-public?
94    pub fn set_truncated(data: &mut [u8]) {
95        let oldflags = BigEndian::read_u16(&data[2..4]);
96        BigEndian::write_u16(&mut data[2..4], oldflags & flag::TRUNCATED);
97    }
98    /// Returns a size of the header (always 12 bytes)
99    pub fn size() -> usize { 12 }
100}
101
102
103#[cfg(test)]
104mod test {
105
106    use {Header};
107    use Opcode::*;
108    use ResponseCode::NoError;
109
110    #[test]
111    fn parse_example_query() {
112        let query = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
113                      \x07example\x03com\x00\x00\x01\x00\x01";
114        let header = Header::parse(query).unwrap();
115        assert_eq!(header, Header {
116            id: 1573,
117            query: true,
118            opcode: StandardQuery,
119            authoritative: false,
120            truncated: false,
121            recursion_desired: true,
122            recursion_available: false,
123            authenticated_data: false,
124            checking_disabled: false,
125            response_code: NoError,
126            questions: 1,
127            answers: 0,
128            nameservers: 0,
129            additional: 0,
130        });
131    }
132
133    #[test]
134    fn parse_example_response() {
135        let response = b"\x06%\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
136                         \x07example\x03com\x00\x00\x01\x00\x01\
137                         \xc0\x0c\x00\x01\x00\x01\x00\x00\x04\xf8\
138                         \x00\x04]\xb8\xd8\"";
139        let header = Header::parse(response).unwrap();
140        assert_eq!(header, Header {
141            id: 1573,
142            query: false,
143            opcode: StandardQuery,
144            authoritative: false,
145            truncated: false,
146            recursion_desired: true,
147            recursion_available: true,
148            authenticated_data: false,
149            checking_disabled: false,
150            response_code: NoError,
151            questions: 1,
152            answers: 1,
153            nameservers: 0,
154            additional: 0,
155        });
156    }
157
158    #[test]
159    fn parse_query_with_ad_set() {
160        let query = b"\x06%\x01\x20\x00\x01\x00\x00\x00\x00\x00\x00\
161                      \x07example\x03com\x00\x00\x01\x00\x01";
162        let header = Header::parse(query).unwrap();
163        assert_eq!(header, Header {
164            id: 1573,
165            query: true,
166            opcode: StandardQuery,
167            authoritative: false,
168            truncated: false,
169            recursion_desired: true,
170            recursion_available: false,
171            authenticated_data: true,
172            checking_disabled: false,
173            response_code: NoError,
174            questions: 1,
175            answers: 0,
176            nameservers: 0,
177            additional: 0,
178        });
179    }
180
181    #[test]
182    fn parse_query_with_cd_set() {
183        let query = b"\x06%\x01\x10\x00\x01\x00\x00\x00\x00\x00\x00\
184                      \x07example\x03com\x00\x00\x01\x00\x01";
185        let header = Header::parse(query).unwrap();
186        assert_eq!(header, Header {
187            id: 1573,
188            query: true,
189            opcode: StandardQuery,
190            authoritative: false,
191            truncated: false,
192            recursion_desired: true,
193            recursion_available: false,
194            authenticated_data: false,
195            checking_disabled: true,
196            response_code: NoError,
197            questions: 1,
198            answers: 0,
199            nameservers: 0,
200            additional: 0,
201        });
202    }
203}