domain_core/bits/query.rs
1//! Message handling for queries.
2//!
3//! While queries appear to be like any other DNS message, they are in fact
4//! special: They have exactly one entry in their question section, empty
5//! answer and authority sections, and optionally an OPT and a TSIG record in
6//! the additional section. In addition, queries may need to be reused – if
7//! an upstream server won’t respond, another server needs to be asked. While
8//! the OPT and TSIG records may need to be changed, the question doesn’t and
9//! can be used again.
10//!
11//! This module provides types that help with creating, using, and re-using
12//! queries. [`QueryBuilder`] allows to construct a query and add and remove
13//! an OPT record as needed. A complete message can be frozen into a
14//! [`QueryMessage`] that can be given to the transport for sending. It can
15//! later be unfrozen back into a `QueryBuilder` for manipulations.
16//!
17//! [`QueryBuilder`]: struct.QueryBuilder.html
18//! [`QueryMessage`]: struct.QueryMessage.html
19
20use std::{mem, ops};
21use bytes::{BigEndian, BufMut, ByteOrder, Bytes, BytesMut};
22use super::compose::Compose;
23use super::header::{Header, HeaderCounts, HeaderSection};
24use super::message::Message;
25use super::name::ToDname;
26use super::opt::{OptData, OptHeader};
27use super::question::Question;
28
29
30//------------ QueryBuilder --------------------------------------------------
31
32/// Builds a query DNS message.
33///
34/// You can create a new query from a given question using the [`new`]
35/// function. The [`add_opt`] method provides the means to add an OPT record
36/// to the additional section. The entire additional section can later be
37/// removed through the [`revert_additional`] function.
38///
39/// Once you are happy with your query, you can turn it into a
40/// [`QueryMessage`] through the [`freeze`] method.
41///
42/// [`freeze`]: #method.freeze
43/// [`new`]: #method.new
44/// [`add_opt`]: #method.add_opt
45/// [`revert_additional`]: #method.revert_additional
46/// [`QueryMessage`]: struct.QueryMessage.html
47#[derive(Clone, Debug)]
48pub struct QueryBuilder {
49 /// The buffer containing the message.
50 ///
51 /// Note that we always build a query for streaming transports which
52 /// means that the first two octets are the length shim.
53 target: BytesMut,
54
55 /// The index in `target` where the additional section starts.
56 additional: usize,
57}
58
59impl QueryBuilder {
60 /// Creates a new query builder.
61 ///
62 /// The query will contain one question built from `question`. It will
63 /// have a random ID. The RD bit will _not_ be set. If you desire
64 /// recursion, you can enable it via the [`set_rd`] method.
65 ///
66 /// [`set_rd`]: #method.set_rd
67 pub fn new<N: ToDname, Q: Into<Question<N>>>(question: Q) -> Self {
68 let mut header = HeaderSection::default();
69 header.header_mut().set_random_id();
70 header.counts_mut().set_qdcount(1);
71 let question = question.into();
72 let len = header.compose_len() + question.compose_len();
73 let mut target = BytesMut::with_capacity(len + 2);
74 target.put_u16_be(len as u16);
75 header.compose(&mut target);
76 question.compose(&mut target);
77 QueryBuilder {
78 additional: target.len(),
79 target
80 }
81 }
82
83 /// Returns a reference to the header of the query.
84 pub fn header(&self) -> &Header {
85 Header::for_message_slice(&self.target.as_ref()[2..])
86 }
87
88 /// Returns a mutable reference to the header of the query.
89 pub fn header_mut(&mut self) -> &mut Header {
90 Header::for_message_slice_mut(&mut self.target.as_mut()[2..])
91 }
92
93 /// Returns a reference to the section counts of the query.
94 fn counts_mut(&mut self) -> &mut HeaderCounts {
95 HeaderCounts::for_message_slice_mut(&mut self.target.as_mut()[2..])
96 }
97
98 /// Sets the ‘recursion desired’ (RD) bit to the given value.
99 ///
100 /// This is a shortcut to `self.header_mut().set_rd(value)`.
101 ///
102 /// By default, this bit is _not_ set.
103 pub fn set_rd(&mut self, value: bool) {
104 self.header_mut().set_rd(value)
105 }
106
107 /// Updates the length shim of the message.
108 ///
109 /// Call this method any time you add or remove octets from the message.
110 fn update_shim(&mut self) {
111 let len = self.target.len() - 2;
112 assert!(len <= ::std::u16::MAX as usize);
113 BigEndian::write_u16(self.target.as_mut(), len as u16);
114 }
115
116 /// Adds an OPT record to the additional section.
117 ///
118 /// The content of the record can be manipulated in the closure provided
119 /// as an argument. This closure receives a mutable reference to an
120 /// [`OptBuilder`] which will allow access to the OPT record’s header as
121 /// well as allow adding options.
122 ///
123 /// [`OptBuilder`]: struct.OptBuilder.html
124 pub fn add_opt<F>(&mut self, op: F)
125 where F: FnOnce(&mut OptBuilder) {
126 op(&mut OptBuilder::new(self))
127 }
128
129 /// Removes all records from the additional section.
130 ///
131 /// Afterwards, only the single question will remain in the message.
132 pub fn revert_additional(&mut self) {
133 self.target.truncate(self.additional);
134 self.counts_mut().set_adcount(0);
135 self.update_shim();
136 }
137
138 /// Freezes the query builder into a query message.
139 pub fn freeze(self) -> QueryMessage {
140 let bytes = self.target.freeze();
141 QueryMessage {
142 message: Message::from_bytes(bytes.slice_from(2)).unwrap(),
143 bytes,
144 additional: self.additional
145 }
146 }
147}
148
149
150//------------ OptBuilder ----------------------------------------------------
151
152/// A builder for the OPT record of a query.
153///
154/// A mutable reference to this type will be passed to the closure given to
155/// [`QueryBuilder::add_opt`]. It allows manipulation of the record’s header
156/// via the [`header_mut`] method and adding of options via [`push`].
157///
158/// # Limitations
159///
160/// Note that currently this type is not compatible with the various option
161/// types‘ `push` functions. This will be addressed soon by redesigning that
162/// mechanism.
163///
164/// [`QueryBuilder::add_opt`]: struct.QueryBuilder.html#method.add_opt
165/// [`header_mut`]: #method.header_mut
166/// [`push`]: #method.push
167#[derive(Debug)]
168pub struct OptBuilder<'a> {
169 /// The query builder we work with.
170 query: &'a mut QueryBuilder,
171
172 /// The index in `query`’s target where the OPT record started.
173 pos: usize,
174}
175
176impl<'a> OptBuilder<'a> {
177 /// Creates a new OPT builder borrowing the given query builder.
178 ///
179 /// The function appends the OPT record’s header to the query, increases
180 /// its ARCOUNT, and recalculates the stream shim.
181 fn new(query: &'a mut QueryBuilder) -> Self {
182 let pos = query.target.len();
183 let header = OptHeader::default();
184 query.target.reserve(header.compose_len() + 2);
185 header.compose(&mut query.target);
186 0u16.compose(&mut query.target);
187 query.counts_mut().inc_arcount();
188 query.update_shim();
189 OptBuilder { query, pos }
190 }
191
192 /// Returns a reference to the header of the OPT record.
193 pub fn header(&self) -> &OptHeader {
194 OptHeader::for_record_slice(&self.query.target.as_ref()[self.pos..])
195 }
196
197 /// Returns a mutable reference to the header of the OPT record.
198 pub fn header_mut(&mut self) -> &mut OptHeader {
199 OptHeader::for_record_slice_mut(&mut self.query.target.as_mut()
200 [self.pos..])
201 }
202
203 /// Appends an option to the OPT record.
204 pub fn push<O: OptData>(&mut self, option: &O) {
205 option.code().compose(&mut self.query.target);
206 let len = option.compose_len();
207 assert!(len <= ::std::u16::MAX.into());
208 (len as u16).compose(&mut self.query.target);
209 option.compose(&mut self.query.target);
210 self.update_length();
211 }
212
213 /// Updates the length of OPT record and the length shim of the query.
214 fn update_length(&mut self) {
215 let len = self.query.target.len()
216 - (self.pos + mem::size_of::<OptHeader>() + 2);
217 assert!(len <= ::std::u16::MAX.into());
218 let count_pos = self.pos + mem::size_of::<OptHeader>();
219 BigEndian::write_u16(
220 &mut self.query.target.as_mut()[count_pos..],
221 len as u16
222 );
223 self.query.update_shim();
224 }
225}
226
227
228//------------ QueryMessage --------------------------------------------------
229
230/// A DNS query message.
231///
232/// A value of this type contains a complete DNS query message ready for
233/// sending. The type derefs to [`Message`] to provide all the functionality
234/// of a regular message.
235///
236/// In order to send the query, the two methods [`as_stream_slice`] and
237/// [`as_dgram_slice`] provide access to raw octets with or without the two
238/// octet length indicator necessary for stream transports such as TCP,
239/// respectively.
240///
241/// Finally, in order to manipulat the message for re-use, the method
242/// [`unfreeze`] returns it into a [`QueryBuilder`].
243///
244/// [`Message`]: ../message/struct.Message.html
245/// [`as_stream_slice`]: #method.as_stream_slice
246/// [`as_dgram_slice`]: #method.as_dgram_slice
247/// [`unfreeze`]: #method.unfreeze
248/// [`QueryBuilder`]: struct.QueryBuilder.html
249#[derive(Clone, Debug)]
250pub struct QueryMessage {
251 /// The complete bytes of the message including the length shim.
252 bytes: Bytes,
253
254 /// The message itself.
255 ///
256 /// This references the same memory as `bytes`.
257 //
258 // XXX We should re-work `Message` s that it can deal with the length
259 // shim natively.
260 message: Message,
261
262 /// The index in `bytes` where the message’s additional section starts.
263 additional: usize
264}
265
266impl QueryMessage {
267 /// Convert the message into a query builder.
268 ///
269 /// If this message has the only reference to the underlying bytes, no
270 /// re-allocation is necessary. Otherwise, the bytes will be copied into
271 /// a new allocation.
272 ///
273 /// The returned builder will have a new, random message ID to make sure
274 /// you don’t accidentally reuse the old one.
275 pub fn unfreeze(self) -> QueryBuilder {
276 drop(self.message);
277 let mut res = QueryBuilder {
278 target: self.bytes.into(),
279 additional: self.additional
280 };
281 res.header_mut().set_random_id();
282 res
283 }
284
285 /// Returns a slice of the message octets including the length shim.
286 ///
287 /// This is suitable for stream transports such as TCP.
288 pub fn as_stream_slice(&self) -> &[u8] {
289 self.bytes.as_ref()
290 }
291
292 /// Returns a slice of the message octets without the length shim.
293 ///
294 /// This is suitable for datagram transports such as UDP.
295 pub fn as_dgram_slice(&self) -> &[u8] {
296 &self.bytes.as_ref()[2..]
297 }
298}
299
300
301//--- Deref and AsRef
302
303impl ops::Deref for QueryMessage {
304 type Target = Message;
305
306 fn deref(&self) -> &Message {
307 &self.message
308 }
309}
310
311impl AsRef<Message> for QueryMessage {
312 fn as_ref(&self) -> &Message {
313 &self.message
314 }
315}
316
317
318//------------ DgramQueryMessage ---------------------------------------------
319
320/// A raw query message for use with datagram transports.
321///
322/// This type wraps a [`QueryMessage`] and provides an `AsRef<[u8]>`
323/// implementation so that it can be passed to Tokio’s
324/// `UdpSocket::send_dgram`.
325#[derive(Clone, Debug)]
326pub struct DgramQueryMessage(QueryMessage);
327
328impl DgramQueryMessage {
329 /// Creates a new datagram query message from a query message.
330 pub fn new(query: QueryMessage) -> DgramQueryMessage {
331 DgramQueryMessage(query)
332 }
333
334 /// Converts the datagram query message back into a query message.
335 pub fn unwrap(self) -> QueryMessage {
336 self.0
337 }
338
339 /// Unfreezes the datagram query message into a query builder.
340 ///
341 /// This is a shortcut for `self.unwrap().unfreeze()`. See
342 /// [`QueryMessage::unfreeze`] for additional information.
343 ///
344 /// [`QueryMessage::unfreeze`]: struct.QueryMessage.html#method.unfreeze
345 pub fn unfreeze(self) -> QueryBuilder {
346 self.unwrap().unfreeze()
347 }
348}
349
350
351//--- From
352
353impl From<QueryMessage> for DgramQueryMessage {
354 fn from(query: QueryMessage) -> DgramQueryMessage {
355 Self::new(query)
356 }
357}
358
359
360//--- AsRef
361
362impl AsRef<[u8]> for DgramQueryMessage {
363 fn as_ref(&self) -> &[u8] {
364 self.0.as_dgram_slice()
365 }
366}
367
368
369
370//------------ StreamQueryMessgae --------------------------------------------
371
372/// A raw query message for use with stream transports.
373///
374/// This type wraps a [`QueryMessage`] and provides an `AsRef<[u8]>`
375/// implementation so that it can be passed to Tokio’s
376/// `write_all`.
377#[derive(Clone, Debug)]
378pub struct StreamQueryMessage(QueryMessage);
379
380impl StreamQueryMessage {
381 /// Creates a new stream query message from a query message.
382 pub fn new(query: QueryMessage) -> StreamQueryMessage {
383 StreamQueryMessage(query)
384 }
385
386 /// Converts the stream query message back into a query message.
387 pub fn unwrap(self) -> QueryMessage {
388 self.0
389 }
390
391 /// Unfreezes the stream query message into a query builder.
392 ///
393 /// This is a shortcut for `self.unwrap().unfreeze()`. See
394 /// [`QueryMessage::unfreeze`] for additional information.
395 ///
396 /// [`QueryMessage::unfreeze`]: struct.QueryMessage.html#method.unfreeze
397 pub fn unfreeze(self) -> QueryBuilder {
398 self.unwrap().unfreeze()
399 }
400}
401
402
403//--- From
404
405impl From<QueryMessage> for StreamQueryMessage {
406 fn from(query: QueryMessage) -> StreamQueryMessage {
407 Self::new(query)
408 }
409}
410
411
412//--- AsRef
413
414impl AsRef<[u8]> for StreamQueryMessage {
415 fn as_ref(&self) -> &[u8] {
416 self.0.as_stream_slice()
417 }
418}
419