Coverage Report

Created: 2025-07-23 07:04

/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
}