/src/hickory-dns/crates/proto/src/op/query.rs
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com> |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * https://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | //! Query struct for looking up resource records |
18 | | |
19 | | #[cfg(test)] |
20 | | use alloc::vec::Vec; |
21 | | use core::fmt::{self, Display, Formatter}; |
22 | | |
23 | | #[cfg(feature = "serde")] |
24 | | use serde::{Deserialize, Serialize}; |
25 | | |
26 | | use crate::error::*; |
27 | | use crate::rr::dns_class::DNSClass; |
28 | | use crate::rr::domain::Name; |
29 | | use crate::rr::record_type::RecordType; |
30 | | use crate::serialize::binary::*; |
31 | | |
32 | | #[cfg(feature = "mdns")] |
33 | | /// From [RFC 6762](https://tools.ietf.org/html/rfc6762#section-5.4) |
34 | | /// ```text |
35 | | // To avoid large floods of potentially unnecessary responses in these |
36 | | // cases, Multicast DNS defines the top bit in the class field of a DNS |
37 | | // question as the unicast-response bit. |
38 | | /// ``` |
39 | | const MDNS_UNICAST_RESPONSE: u16 = 1 << 15; |
40 | | |
41 | | /// Query struct for looking up resource records, basically a resource record without RDATA. |
42 | | /// |
43 | | /// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035) |
44 | | /// |
45 | | /// ```text |
46 | | /// 4.1.2. Question section format |
47 | | /// |
48 | | /// The question section is used to carry the "question" in most queries, |
49 | | /// i.e., the parameters that define what is being asked. The section |
50 | | /// contains QDCOUNT (usually 1) entries, each of the following format: |
51 | | /// |
52 | | /// 1 1 1 1 1 1 |
53 | | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
54 | | /// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
55 | | /// | | |
56 | | /// / QNAME / ZNAME / |
57 | | /// / / |
58 | | /// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
59 | | /// | QTYPE / ZTYPE | |
60 | | /// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
61 | | /// | QCLASS / ZCLASS | |
62 | | /// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
63 | | /// |
64 | | /// ``` |
65 | | #[derive(Clone, Debug, Eq, Hash, PartialEq)] |
66 | | #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] |
67 | | #[non_exhaustive] |
68 | | pub struct Query { |
69 | | /// QNAME |
70 | | pub name: Name, |
71 | | |
72 | | /// QTYPE |
73 | | pub query_type: RecordType, |
74 | | |
75 | | /// QCLASS |
76 | | pub query_class: DNSClass, |
77 | | |
78 | | /// mDNS unicast-response bit set or not |
79 | | #[cfg(feature = "mdns")] |
80 | | pub mdns_unicast_response: bool, |
81 | | } |
82 | | |
83 | | impl Default for Query { |
84 | | /// Return a default query with an empty name and A, IN for the query_type and query_class |
85 | 0 | fn default() -> Self { |
86 | 0 | Self { |
87 | 0 | name: Name::root(), |
88 | 0 | query_type: RecordType::A, |
89 | 0 | query_class: DNSClass::IN, |
90 | 0 | #[cfg(feature = "mdns")] |
91 | 0 | mdns_unicast_response: false, |
92 | 0 | } |
93 | 0 | } |
94 | | } |
95 | | |
96 | | impl Query { |
97 | | /// Return a default query with an empty name and A, IN for the query_type and query_class |
98 | 0 | pub fn new() -> Self { |
99 | 0 | Self::default() |
100 | 0 | } |
101 | | |
102 | | /// Create a new query from name and type, class defaults to IN |
103 | | #[allow(clippy::self_named_constructors)] |
104 | 0 | pub fn query(name: Name, query_type: RecordType) -> Self { |
105 | 0 | Self { |
106 | 0 | name, |
107 | 0 | query_type, |
108 | 0 | query_class: DNSClass::IN, |
109 | 0 | #[cfg(feature = "mdns")] |
110 | 0 | mdns_unicast_response: false, |
111 | 0 | } |
112 | 0 | } |
113 | | |
114 | | /// replaces name with the new name |
115 | 0 | pub fn set_name(&mut self, name: Name) -> &mut Self { |
116 | 0 | self.name = name; |
117 | 0 | self |
118 | 0 | } |
119 | | |
120 | | /// Specify the RecordType being queried |
121 | 0 | pub fn set_query_type(&mut self, query_type: RecordType) -> &mut Self { |
122 | 0 | self.query_type = query_type; |
123 | 0 | self |
124 | 0 | } |
125 | | |
126 | | /// Specify÷ the DNS class of the Query, almost always IN |
127 | 0 | pub fn set_query_class(&mut self, query_class: DNSClass) -> &mut Self { |
128 | 0 | self.query_class = query_class; |
129 | 0 | self |
130 | 0 | } |
131 | | |
132 | | /// Changes mDNS unicast-response bit |
133 | | /// See [RFC 6762](https://tools.ietf.org/html/rfc6762#section-5.4) |
134 | | #[cfg(feature = "mdns")] |
135 | | pub fn set_mdns_unicast_response(&mut self, flag: bool) -> &mut Self { |
136 | | self.mdns_unicast_response = flag; |
137 | | self |
138 | | } |
139 | | |
140 | | /// ```text |
141 | | /// QNAME a domain name represented as a sequence of labels, where |
142 | | /// each label consists of a length octet followed by that |
143 | | /// number of octets. The domain name terminates with the |
144 | | /// zero length octet for the null label of the root. Note |
145 | | /// that this field may be an odd number of octets; no |
146 | | /// padding is used. |
147 | | /// ``` |
148 | 0 | pub fn name(&self) -> &Name { |
149 | 0 | &self.name |
150 | 0 | } |
151 | | |
152 | | /// ```text |
153 | | /// QTYPE a two octet code which specifies the type of the query. |
154 | | /// The values for this field include all codes valid for a |
155 | | /// TYPE field, together with some more general codes which |
156 | | /// can match more than one type of RR. |
157 | | /// ``` |
158 | 0 | pub fn query_type(&self) -> RecordType { |
159 | 0 | self.query_type |
160 | 0 | } |
161 | | |
162 | | /// ```text |
163 | | /// QCLASS a two octet code that specifies the class of the query. |
164 | | /// For example, the QCLASS field is IN for the Internet. |
165 | | /// ``` |
166 | 0 | pub fn query_class(&self) -> DNSClass { |
167 | 0 | self.query_class |
168 | 0 | } |
169 | | |
170 | | /// Returns if the mDNS unicast-response bit is set or not |
171 | | /// See [RFC 6762](https://tools.ietf.org/html/rfc6762#section-5.4) |
172 | | #[cfg(feature = "mdns")] |
173 | | pub fn mdns_unicast_response(&self) -> bool { |
174 | | self.mdns_unicast_response |
175 | | } |
176 | | } |
177 | | |
178 | | impl BinEncodable for Query { |
179 | 259k | fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> { |
180 | 259k | self.name.emit(encoder)?; |
181 | 259k | self.query_type.emit(encoder)?; |
182 | | |
183 | | #[cfg(not(feature = "mdns"))] |
184 | 259k | self.query_class.emit(encoder)?; |
185 | | |
186 | | #[cfg(feature = "mdns")] |
187 | | { |
188 | | if self.mdns_unicast_response { |
189 | | encoder.emit_u16(u16::from(self.query_class()) | MDNS_UNICAST_RESPONSE)?; |
190 | | } else { |
191 | | self.query_class.emit(encoder)?; |
192 | | } |
193 | | } |
194 | | |
195 | 259k | Ok(()) |
196 | 259k | } |
197 | | } |
198 | | |
199 | | impl<'r> BinDecodable<'r> for Query { |
200 | 539k | fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> { |
201 | 539k | let name = Name::read(decoder)?; |
202 | 538k | let query_type = RecordType::read(decoder)?; |
203 | | |
204 | | #[cfg(feature = "mdns")] |
205 | | let mut mdns_unicast_response = false; |
206 | | |
207 | | #[cfg(not(feature = "mdns"))] |
208 | 538k | let query_class = DNSClass::read(decoder)?; |
209 | | |
210 | | #[cfg(feature = "mdns")] |
211 | | let query_class = { |
212 | | let query_class_value = |
213 | | decoder.read_u16()?.unverified(/*DNSClass::from_u16 will verify the value*/); |
214 | | if query_class_value & MDNS_UNICAST_RESPONSE > 0 { |
215 | | mdns_unicast_response = true; |
216 | | DNSClass::from(query_class_value & !MDNS_UNICAST_RESPONSE) |
217 | | } else { |
218 | | DNSClass::from(query_class_value) |
219 | | } |
220 | | }; |
221 | | |
222 | 538k | Ok(Self { |
223 | 538k | name, |
224 | 538k | query_type, |
225 | 538k | query_class, |
226 | 538k | #[cfg(feature = "mdns")] |
227 | 538k | mdns_unicast_response, |
228 | 538k | }) |
229 | 539k | } |
230 | | } |
231 | | |
232 | | impl Display for Query { |
233 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { |
234 | 0 | #[cfg(not(feature = "mdns"))] |
235 | 0 | { |
236 | 0 | write!( |
237 | 0 | f, |
238 | 0 | "{name} {class} {ty}", |
239 | 0 | name = self.name, |
240 | 0 | class = self.query_class, |
241 | 0 | ty = self.query_type, |
242 | 0 | ) |
243 | 0 | } |
244 | 0 |
|
245 | 0 | #[cfg(feature = "mdns")] |
246 | 0 | { |
247 | 0 | write!( |
248 | 0 | f, |
249 | 0 | "{name} {class} {ty}; mdns_unicast_response: {mdns}", |
250 | 0 | name = self.name, |
251 | 0 | class = self.query_class, |
252 | 0 | ty = self.query_type, |
253 | 0 | mdns = self.mdns_unicast_response |
254 | 0 | ) |
255 | 0 | } |
256 | 0 | } |
257 | | } |
258 | | |
259 | | #[test] |
260 | | fn test_read_and_emit() { |
261 | | let expect = Query { |
262 | | name: Name::from_ascii("WWW.example.com.").unwrap(), |
263 | | query_type: RecordType::AAAA, |
264 | | query_class: DNSClass::IN, |
265 | | ..Query::default() |
266 | | }; |
267 | | |
268 | | let mut byte_vec: Vec<u8> = Vec::with_capacity(512); |
269 | | { |
270 | | let mut encoder = BinEncoder::new(&mut byte_vec); |
271 | | expect.emit(&mut encoder).unwrap(); |
272 | | } |
273 | | |
274 | | let mut decoder = BinDecoder::new(&byte_vec); |
275 | | let got = Query::read(&mut decoder).unwrap(); |
276 | | assert_eq!(got, expect); |
277 | | } |
278 | | |
279 | | #[cfg(feature = "mdns")] |
280 | | #[test] |
281 | | fn test_mdns_unicast_response_bit_handling() { |
282 | | const QCLASS_OFFSET: usize = 1 /* empty name */ + |
283 | | core::mem::size_of::<u16>() /* query_type */; |
284 | | |
285 | | let mut query = Query::new(); |
286 | | query.set_mdns_unicast_response(true); |
287 | | |
288 | | let mut vec_bytes: Vec<u8> = Vec::with_capacity(512); |
289 | | { |
290 | | let mut encoder = BinEncoder::new(&mut vec_bytes); |
291 | | query.emit(&mut encoder).unwrap(); |
292 | | |
293 | | let query_class_slice = encoder.slice_of(QCLASS_OFFSET, QCLASS_OFFSET + 2); |
294 | | assert_eq!(query_class_slice, &[0x80, 0x01]); |
295 | | } |
296 | | |
297 | | let mut decoder = BinDecoder::new(&vec_bytes); |
298 | | |
299 | | let got = Query::read(&mut decoder).unwrap(); |
300 | | |
301 | | assert_eq!(got.query_class(), DNSClass::IN); |
302 | | assert!(got.mdns_unicast_response()); |
303 | | } |