hickory_proto/op/
query.rs

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)]
20use alloc::vec::Vec;
21use core::fmt::{self, Display, Formatter};
22
23#[cfg(feature = "serde")]
24use serde::{Deserialize, Serialize};
25
26use crate::error::*;
27use crate::rr::dns_class::DNSClass;
28use crate::rr::domain::Name;
29use crate::rr::record_type::RecordType;
30use 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/// ```
39const 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]
68pub 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
83impl Default for Query {
84    /// Return a default query with an empty name and A, IN for the query_type and query_class
85    fn default() -> Self {
86        Self {
87            name: Name::root(),
88            query_type: RecordType::A,
89            query_class: DNSClass::IN,
90            #[cfg(feature = "mdns")]
91            mdns_unicast_response: false,
92        }
93    }
94}
95
96impl Query {
97    /// Return a default query with an empty name and A, IN for the query_type and query_class
98    pub fn new() -> Self {
99        Self::default()
100    }
101
102    /// Create a new query from name and type, class defaults to IN
103    #[allow(clippy::self_named_constructors)]
104    pub fn query(name: Name, query_type: RecordType) -> Self {
105        Self {
106            name,
107            query_type,
108            query_class: DNSClass::IN,
109            #[cfg(feature = "mdns")]
110            mdns_unicast_response: false,
111        }
112    }
113
114    /// replaces name with the new name
115    pub fn set_name(&mut self, name: Name) -> &mut Self {
116        self.name = name;
117        self
118    }
119
120    /// Specify the RecordType being queried
121    pub fn set_query_type(&mut self, query_type: RecordType) -> &mut Self {
122        self.query_type = query_type;
123        self
124    }
125
126    /// Specify÷ the DNS class of the Query, almost always IN
127    pub fn set_query_class(&mut self, query_class: DNSClass) -> &mut Self {
128        self.query_class = query_class;
129        self
130    }
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    pub fn name(&self) -> &Name {
149        &self.name
150    }
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    pub fn query_type(&self) -> RecordType {
159        self.query_type
160    }
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    pub fn query_class(&self) -> DNSClass {
167        self.query_class
168    }
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
178impl BinEncodable for Query {
179    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
180        self.name.emit(encoder)?;
181        self.query_type.emit(encoder)?;
182
183        #[cfg(not(feature = "mdns"))]
184        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        Ok(())
196    }
197}
198
199impl<'r> BinDecodable<'r> for Query {
200    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
201        let name = Name::read(decoder)?;
202        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        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        Ok(Self {
223            name,
224            query_type,
225            query_class,
226            #[cfg(feature = "mdns")]
227            mdns_unicast_response,
228        })
229    }
230}
231
232impl Display for Query {
233    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
234        #[cfg(not(feature = "mdns"))]
235        {
236            write!(
237                f,
238                "{name} {class} {ty}",
239                name = self.name,
240                class = self.query_class,
241                ty = self.query_type,
242            )
243        }
244
245        #[cfg(feature = "mdns")]
246        {
247            write!(
248                f,
249                "{name} {class} {ty}; mdns_unicast_response: {mdns}",
250                name = self.name,
251                class = self.query_class,
252                ty = self.query_type,
253                mdns = self.mdns_unicast_response
254            )
255        }
256    }
257}
258
259#[test]
260#[allow(clippy::needless_update)]
261fn test_read_and_emit() {
262    let expect = Query {
263        name: Name::from_ascii("WWW.example.com.").unwrap(),
264        query_type: RecordType::AAAA,
265        query_class: DNSClass::IN,
266        ..Query::default()
267    };
268
269    let mut byte_vec: Vec<u8> = Vec::with_capacity(512);
270    {
271        let mut encoder = BinEncoder::new(&mut byte_vec);
272        expect.emit(&mut encoder).unwrap();
273    }
274
275    let mut decoder = BinDecoder::new(&byte_vec);
276    let got = Query::read(&mut decoder).unwrap();
277    assert_eq!(got, expect);
278}
279
280#[cfg(feature = "mdns")]
281#[test]
282fn test_mdns_unicast_response_bit_handling() {
283    const QCLASS_OFFSET: usize = 1 /* empty name */ +
284        core::mem::size_of::<u16>() /* query_type */;
285
286    let mut query = Query::new();
287    query.set_mdns_unicast_response(true);
288
289    let mut vec_bytes: Vec<u8> = Vec::with_capacity(512);
290    {
291        let mut encoder = BinEncoder::new(&mut vec_bytes);
292        query.emit(&mut encoder).unwrap();
293
294        let query_class_slice = encoder.slice_of(QCLASS_OFFSET, QCLASS_OFFSET + 2);
295        assert_eq!(query_class_slice, &[0x80, 0x01]);
296    }
297
298    let mut decoder = BinDecoder::new(&vec_bytes);
299
300    let got = Query::read(&mut decoder).unwrap();
301
302    assert_eq!(got.query_class(), DNSClass::IN);
303    assert!(got.mdns_unicast_response());
304}