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)]
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 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}