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