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#[derive(Debug, PartialEq, Eq, Clone, Copy)]
20#[allow(missing_docs)] pub 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 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 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 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 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}