Coverage Report

Created: 2026-09-04 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hickory-dns/crates/proto/src/op/query.rs
Line
Count
Source
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::rr::{Record, RecordData};
31
use crate::serialize::binary::*;
32
33
#[cfg(feature = "mdns")]
34
/// From [RFC 6762](https://tools.ietf.org/html/rfc6762#section-5.4)
35
/// ```text
36
// To avoid large floods of potentially unnecessary responses in these
37
// cases, Multicast DNS defines the top bit in the class field of a DNS
38
// question as the unicast-response bit.
39
/// ```
40
const MDNS_UNICAST_RESPONSE: u16 = 1 << 15;
41
42
/// Query struct for looking up resource records, basically a resource record without RDATA.
43
///
44
/// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
45
///
46
/// ```text
47
/// 4.1.2. Question section format
48
///
49
/// The question section is used to carry the "question" in most queries,
50
/// i.e., the parameters that define what is being asked.  The section
51
/// contains QDCOUNT (usually 1) entries, each of the following format:
52
///
53
///                                     1  1  1  1  1  1
54
///       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
55
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
56
///     |                                               |
57
///     /                     QNAME / ZNAME             /
58
///     /                                               /
59
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
60
///     |                     QTYPE / ZTYPE             |
61
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
62
///     |                     QCLASS / ZCLASS           |
63
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
64
///
65
/// ```
66
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
67
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
68
#[non_exhaustive]
69
pub struct Query {
70
    /// QNAME
71
    pub name: Name,
72
73
    /// QTYPE
74
    pub query_type: RecordType,
75
76
    /// QCLASS
77
    pub query_class: DNSClass,
78
79
    /// mDNS unicast-response bit set or not
80
    #[cfg(feature = "mdns")]
81
    pub mdns_unicast_response: bool,
82
}
83
84
impl Query {
85
    /// Return a default query with an empty name and A, IN for the query_type and query_class
86
0
    pub fn root() -> Self {
87
0
        Self::new(Name::root(), RecordType::A)
88
0
    }
89
90
    /// Create a new query from name and type, class defaults to IN
91
0
    pub fn new(name: Name, query_type: RecordType) -> Self {
92
0
        Self {
93
0
            name,
94
0
            query_type,
95
0
            query_class: DNSClass::IN,
96
0
            #[cfg(feature = "mdns")]
97
0
            mdns_unicast_response: false,
98
0
        }
99
0
    }
100
101
    /// replaces name with the new name
102
0
    pub fn set_name(&mut self, name: Name) -> &mut Self {
103
0
        self.name = name;
104
0
        self
105
0
    }
106
107
    /// Specify the RecordType being queried
108
0
    pub fn set_query_type(&mut self, query_type: RecordType) -> &mut Self {
109
0
        self.query_type = query_type;
110
0
        self
111
0
    }
112
113
    /// Specify÷ the DNS class of the Query, almost always IN
114
0
    pub fn set_query_class(&mut self, query_class: DNSClass) -> &mut Self {
115
0
        self.query_class = query_class;
116
0
        self
117
0
    }
118
119
    /// Changes mDNS unicast-response bit
120
    /// See [RFC 6762](https://tools.ietf.org/html/rfc6762#section-5.4)
121
    #[cfg(feature = "mdns")]
122
    pub fn set_mdns_unicast_response(&mut self, flag: bool) -> &mut Self {
123
        self.mdns_unicast_response = flag;
124
        self
125
    }
126
127
    /// Determines if a record is responsive to this
128
0
    pub fn matches_record<T: RecordData>(&self, record: &Record<T>) -> bool {
129
0
        self.name == record.name
130
0
            && (self.query_type == record.record_type()
131
0
                || record.record_type() == RecordType::CNAME)
132
0
            && self.query_class == record.dns_class
133
0
    }
134
}
135
136
impl BinEncodable for Query {
137
355k
    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
138
355k
        self.name.emit(encoder)?;
139
355k
        self.query_type.emit(encoder)?;
140
141
        #[cfg(not(feature = "mdns"))]
142
355k
        self.query_class.emit(encoder)?;
143
144
        #[cfg(feature = "mdns")]
145
        {
146
            if self.mdns_unicast_response {
147
                (u16::from(self.query_class) | MDNS_UNICAST_RESPONSE).emit(encoder)?;
148
            } else {
149
                self.query_class.emit(encoder)?;
150
            }
151
        }
152
153
355k
        Ok(())
154
355k
    }
155
}
156
157
impl<'r> BinDecodable<'r> for Query {
158
1.08M
    fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
159
1.08M
        let name = Name::read(decoder)?;
160
1.08M
        let query_type = RecordType::read(decoder)?;
161
162
        #[cfg(feature = "mdns")]
163
        let mut mdns_unicast_response = false;
164
165
        #[cfg(not(feature = "mdns"))]
166
1.08M
        let query_class = DNSClass::read(decoder)?;
167
168
        #[cfg(feature = "mdns")]
169
        let query_class = {
170
            let query_class_value =
171
                decoder.read_u16()?.unverified(/*DNSClass::from_u16 will verify the value*/);
172
            if query_class_value & MDNS_UNICAST_RESPONSE > 0 {
173
                mdns_unicast_response = true;
174
                DNSClass::from(query_class_value & !MDNS_UNICAST_RESPONSE)
175
            } else {
176
                DNSClass::from(query_class_value)
177
            }
178
        };
179
180
1.08M
        Ok(Self {
181
1.08M
            name,
182
1.08M
            query_type,
183
1.08M
            query_class,
184
1.08M
            #[cfg(feature = "mdns")]
185
1.08M
            mdns_unicast_response,
186
1.08M
        })
187
1.08M
    }
188
}
189
190
impl Display for Query {
191
0
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
192
        #[cfg(not(feature = "mdns"))]
193
        {
194
0
            write!(
195
0
                f,
196
0
                "{name} {class} {ty}",
197
                name = self.name,
198
                class = self.query_class,
199
                ty = self.query_type,
200
            )
201
        }
202
203
        #[cfg(feature = "mdns")]
204
        {
205
            write!(
206
                f,
207
                "{name} {class} {ty}; mdns_unicast_response: {mdns}",
208
                name = self.name,
209
                class = self.query_class,
210
                ty = self.query_type,
211
                mdns = self.mdns_unicast_response
212
            )
213
        }
214
0
    }
215
}
216
217
#[cfg(test)]
218
mod tests {
219
    use super::*;
220
221
    #[test]
222
    fn test_read_and_emit() {
223
        let expect = Query {
224
            name: Name::from_ascii("WWW.example.com.").unwrap(),
225
            query_type: RecordType::AAAA,
226
            query_class: DNSClass::IN,
227
            ..Query::root()
228
        };
229
230
        let mut byte_vec: Vec<u8> = Vec::with_capacity(512);
231
        {
232
            let mut encoder = BinEncoder::new(&mut byte_vec);
233
            expect.emit(&mut encoder).unwrap();
234
        }
235
236
        let mut decoder = BinDecoder::new(&byte_vec);
237
        let got = Query::read(&mut decoder).unwrap();
238
        assert_eq!(got, expect);
239
    }
240
241
    #[cfg(feature = "mdns")]
242
    #[test]
243
    fn test_mdns_unicast_response_bit_handling() {
244
        const QCLASS_OFFSET: usize = 1 /* empty name */ +
245
        size_of::<u16>() /* query_type */;
246
247
        let mut query = Query::root();
248
        query.set_mdns_unicast_response(true);
249
250
        let mut vec_bytes: Vec<u8> = Vec::with_capacity(512);
251
        {
252
            let mut encoder = BinEncoder::new(&mut vec_bytes);
253
            query.emit(&mut encoder).unwrap();
254
255
            let query_class_slice = encoder.slice_of(QCLASS_OFFSET, QCLASS_OFFSET + 2);
256
            assert_eq!(query_class_slice, &[0x80, 0x01]);
257
        }
258
259
        let mut decoder = BinDecoder::new(&vec_bytes);
260
261
        let got = Query::read(&mut decoder).unwrap();
262
263
        assert_eq!(got.query_class, DNSClass::IN);
264
        assert!(got.mdns_unicast_response);
265
    }
266
}