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#[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)
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 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 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 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}