dominion_parser/
body.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5/// Domain name structure and funtions
6pub mod name;
7
8use crate::binutils::*;
9use crate::body::name::Name;
10use crate::ParseError;
11use std::borrow::Cow;
12use std::net::{Ipv4Addr, Ipv6Addr};
13use std::str;
14
15const INIT_RR_SIZE: usize = 64;
16
17macro_rules! types {
18    (
19        $(
20            #[$inner:meta]
21            $variant:tt = $value:literal
22        )+
23    ) => {
24        /// The type of [ResourceRecord].
25        #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
26        pub enum Type {
27            $(
28                #[$inner]
29                $variant,
30            )*
31            /// ?: A value has been received that does not correspond to any known qtype.
32            Unknown(u16),
33        }
34
35        impl TryFrom<QType> for Type {
36            type Error = &'static str;
37
38            #[inline]
39            fn try_from(value: QType) -> Result<Self, Self::Error> {
40                match value {
41                    $(QType::$variant => Ok(Self::$variant),)*
42                    QType::Unknown(n) => Ok(Self::Unknown(n)),
43                    _ => Err("QType is not a valid Type")
44                }
45            }
46        }
47
48        impl From<u16> for Type {
49            #[inline]
50            fn from(value: u16) -> Self {
51                match value {
52                    $($value => Self::$variant,)*
53                    _ => Self::Unknown(value),
54                }
55            }
56        }
57
58        impl From<Type> for u16 {
59            #[inline]
60            fn from(value: Type) -> Self {
61                match value {
62                    $(Type::$variant => $value,)*
63                    Type::Unknown(n) => n,
64                }
65            }
66        }
67
68        /// The type of [Question].
69        #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
70        pub enum QType {
71            $(
72                #[$inner]
73                $variant,
74            )*
75            /// All types
76            All,
77            /// ?: A value has been received that does not correspond to any known qtype.
78            Unknown(u16),
79        }
80
81        impl From<Type> for QType {
82            #[inline]
83            fn from(value: Type) -> Self {
84                match value {
85                    $(Type::$variant => Self::$variant,)*
86                    Type::Unknown(n) => Self::Unknown(n),
87                }
88            }
89        }
90
91        impl From<u16> for QType {
92            #[inline]
93            fn from(value: u16) -> Self {
94                match value {
95                    $($value => Self::$variant,)*
96                    255 => Self::All,
97                    _ => Self::Unknown(value),
98                }
99            }
100        }
101
102        impl From<QType> for u16 {
103            #[inline]
104            fn from(value: QType) -> Self {
105                match value {
106                    $(QType::$variant => $value,)*
107                    QType::All => 255,
108                    QType::Unknown(n) => n,
109                }
110            }
111        }
112    };
113}
114
115/// A query for a [ResourceRecord] of the specified [QType] and [Class].
116///
117/// ```text
118///    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
119///    |                                               |
120///    /                     QNAME                     /
121///    /                                               /
122///    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
123///    |                     QTYPE                     |
124///    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
125///    |                     QCLASS                    |
126///    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
127/// ```
128#[derive(Clone, Debug)]
129pub struct Question<'a> {
130    /// The domain name to be queried
131    pub name: Name<'a>,
132    /// The type of [ResourceRecord] being queried
133    pub qtype: QType,
134    /// The class of [ResourceRecord] being queried
135    pub class: Class,
136}
137
138impl From<Question<'_>> for Vec<u8> {
139    #[inline]
140    fn from(question: Question<'_>) -> Self {
141        let mut out = question.name.into();
142        push_u16(&mut out, question.qtype.into());
143        push_u16(&mut out, question.class.into());
144        out
145    }
146}
147
148impl<'a> Question<'a> {
149    /// Parse from the specified `buff`, starting at position `start`.
150    ///
151    /// # Errors
152    ///
153    /// It will error if the buffer does not contain a valid question. If the domain name
154    /// in the question has been compressed the buffer should include all previous bytes from
155    /// the DNS packet to be considered valid.
156    #[inline]
157    pub fn parse(buff: &'a [u8], start: usize) -> Result<(Self, usize), crate::ParseError> {
158        let (name, size) = Name::parse(buff, start)?;
159        let n = start + size;
160        Ok((
161            Question {
162                name,
163                qtype: safe_u16_read(buff, n)?.into(),
164                class: safe_u16_read(buff, n + 2)?.into(),
165            },
166            size + 4,
167        ))
168    }
169
170    /// Serialize the [Question] and append it tho the end of the provided `packet`
171    #[inline]
172    pub fn serialize(&self, packet: &mut Vec<u8>) {
173        self.name.serialize(packet);
174        push_u16(packet, self.qtype.into());
175        push_u16(packet, self.class.into());
176    }
177}
178
179/// A description of a resource that can be used as an answer to a question
180/// or to provide additional information in the `authority` or `additional` fields
181/// of a DNS packet.
182///
183/// ```text
184///    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
185///    |                                               |
186///    /                                               /
187///    /                      NAME                     /
188///    |                                               |
189///    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
190///    |                      TYPE                     |
191///    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
192///    |                     CLASS                     |
193///    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
194///    |                      TTL                      |
195///    |                                               |
196///    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
197///    |                   RDLENGTH                    |
198///    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
199///    /                     RDATA                     /
200///    /                                               /
201///    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
202/// ```
203#[derive(Debug, Clone)]
204pub struct ResourceRecord<'a> {
205    /// Contains general information that every [ResourceRecord] shares, like type or class.
206    pub preamble: RecordPreamble<'a>,
207    /// The RDATA section of a resource record in some DNS packet.
208    pub data: RecordData<'a>,
209}
210
211impl From<ResourceRecord<'_>> for Vec<u8> {
212    #[inline]
213    fn from(rr: ResourceRecord<'_>) -> Self {
214        let mut out = Vec::with_capacity(INIT_RR_SIZE);
215        rr.serialize(&mut out);
216        out
217    }
218}
219
220impl<'a> ResourceRecord<'a> {
221    /// Parse from the specified `buff`, starting at position `pos`.
222    #[inline]
223    pub fn parse(buff: &'a [u8], pos: usize) -> Result<(Self, usize), ParseError> {
224        let (preamble, size) = RecordPreamble::parse(buff, pos)?;
225        let (data, len) = RecordData::parse(buff, pos + size, &preamble)?;
226        Ok((Self { preamble, data }, size + len))
227    }
228
229    /// Serialize the [ResourceRecord] and append it tho the end of the provided `packet`
230    #[inline]
231    pub fn serialize(&self, packet: &mut Vec<u8>) {
232        self.preamble.serialize(packet);
233        self.data.serialize(packet);
234    }
235}
236
237/// The [ResourceRecord] preamble. Common data to all resource record types.
238#[derive(Debug, Clone)]
239pub struct RecordPreamble<'a> {
240    /// The domain name the RR refers to.
241    pub name: Name<'a>,
242    /// The RR type.
243    pub rrtype: Type,
244    /// The RR class.
245    pub class: Class,
246    /// The time interval that the resource record may be cached before the source of the information should again be consulted.
247    pub ttl: i32,
248    /// The length of the RR data.
249    pub rdlen: u16,
250}
251
252impl<'a> RecordPreamble<'a> {
253    #[inline]
254    fn parse(buff: &'a [u8], pos: usize) -> Result<(Self, usize), ParseError> {
255        let (name, size) = Name::parse(buff, pos)?;
256        let n = size + pos;
257        Ok((
258            RecordPreamble {
259                name,
260                rrtype: safe_u16_read(buff, n)?.into(),
261                class: safe_u16_read(buff, n + 2)?.into(),
262                ttl: safe_i32_read(buff, n + 4)?,
263                rdlen: safe_u16_read(buff, n + 8)?,
264            },
265            size + 10,
266        ))
267    }
268
269    #[inline]
270    fn serialize(&self, packet: &mut Vec<u8>) {
271        self.name.serialize(packet);
272        push_u16(packet, self.rrtype.into());
273        push_u16(packet, self.class.into());
274        push_i32(packet, self.ttl);
275        push_u16(packet, self.rdlen);
276    }
277}
278
279/// The [ResourceRecord] data associated with the corresponding [Name].
280#[non_exhaustive]
281#[derive(Debug, Clone)]
282pub enum RecordData<'a> {
283    /// A host address.
284    A(Ipv4Addr),
285    /// An authoritative name server
286    Ns(Name<'a>),
287    /// The canonical name for an alias.
288    Cname(Name<'a>),
289    /// Mail exchange.
290    Mx {
291        /// The preference given to this RR among others at the same owner.
292        preference: u16,
293        /// A host willing to act as a mail exchange for the owner name.
294        exchange: Name<'a>,
295    },
296    /// Text strings
297    Txt(Cow<'a, str>),
298    /// A host address IPv6
299    Aaaa(Ipv6Addr),
300    /// ?: A value has been received that does not correspond to any known type.
301    Unknown(Cow<'a, [u8]>),
302}
303
304impl<'a> RecordData<'a> {
305    #[inline]
306    fn parse(
307        buff: &'a [u8],
308        pos: usize,
309        rrpreamble: &RecordPreamble<'_>,
310    ) -> Result<(Self, usize), ParseError> {
311        match rrpreamble.rrtype {
312            Type::A => Ok((Self::A(safe_ipv4_read(buff, pos)?), 4)),
313            Type::Ns => {
314                let (name, n) = Name::parse(buff, pos)?;
315                Ok((Self::Ns(name), n))
316            }
317            Type::Cname => {
318                let (name, n) = Name::parse(buff, pos)?;
319                Ok((Self::Cname(name), n))
320            }
321            Type::Mx => {
322                let (exchange, n) = Name::parse(buff, pos + 2)?;
323                Ok((
324                    Self::Mx {
325                        preference: safe_u16_read(buff, pos)?,
326                        exchange,
327                    },
328                    n + 2,
329                ))
330            }
331            Type::Txt => {
332                let len = safe_u8_read(buff, pos)?;
333                let str_bytes = str::from_utf8(&buff[pos..pos + len as usize])?;
334                Ok((Self::Txt(Cow::from(str_bytes)), len as _))
335            }
336            Type::Aaaa => Ok((Self::Aaaa(safe_ipv6_read(buff, pos)?), 16)),
337            Type::Unknown(_) => {
338                let len = rrpreamble.rdlen as _;
339                let end = pos + len;
340                if buff.len() < end {
341                    Err(ParseError::OobRead(end))?
342                }
343                let cow_bytes = Cow::from(&buff[pos..end]);
344                Ok((Self::Unknown(cow_bytes), len))
345            }
346        }
347    }
348
349    #[inline]
350    fn serialize(&self, packet: &mut Vec<u8>) {
351        use std::ops::Deref;
352        match self {
353            Self::A(ip) => packet.extend(ip.octets()),
354            Self::Ns(name) => name.serialize(packet),
355            Self::Cname(name) => name.serialize(packet),
356            Self::Mx {
357                preference,
358                exchange,
359            } => {
360                push_u16(packet, *preference);
361                exchange.serialize(packet);
362            }
363            Self::Txt(txt) => {
364                // <character-string> is a single length octet followed by that number of characters.
365                // <character-string> is treated as binary information, and can be up to 256 characters in
366                // length (including the length octet).
367                packet.push(txt.len() as _);
368                packet.extend(txt.as_bytes());
369            }
370            Self::Aaaa(ip) => packet.extend(ip.octets()),
371            Self::Unknown(buff) => packet.extend(buff.deref()),
372        }
373    }
374}
375
376types! {
377    /// A host address (IPv4)
378    A = 1
379    /// An authoritative name server
380    Ns = 2
381    /// The canonical name for an alias
382    Cname = 5
383    /// A mail exchange
384    Mx = 15
385    /// Text strings
386    Txt = 16
387    /// A host address (IPv6)
388    Aaaa = 28
389}
390
391/// An enumeration of the different available DNS Classes.
392///
393/// In practice should allways be `Class::IN`, but the rest are included for completeness.
394#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
395pub enum Class {
396    /// IN: the Internet
397    IN,
398    /// CS: the CSNET class (Obsolete)
399    CS,
400    /// CH: the CHAOS class
401    CH,
402    /// HS: Hesiod [Dyer 87]
403    HS,
404    /// *: any class
405    Any,
406    /// ?: A value has been received that does not correspond to any known class
407    Unknown(u16),
408}
409
410impl From<u16> for Class {
411    #[inline]
412    fn from(value: u16) -> Self {
413        match value {
414            1 => Self::IN,
415            2 => Self::CS,
416            3 => Self::CH,
417            4 => Self::HS,
418            255 => Self::Any,
419            _ => Self::Unknown(value),
420        }
421    }
422}
423
424impl From<Class> for u16 {
425    #[inline]
426    fn from(value: Class) -> Self {
427        match value {
428            Class::IN => 1,
429            Class::CS => 2,
430            Class::CH => 3,
431            Class::HS => 4,
432            Class::Any => 255,
433            Class::Unknown(n) => n,
434        }
435    }
436}
437
438#[cfg(test)]
439mod tests {
440    use super::*;
441
442    #[test]
443    fn class_transformations() {
444        assert_eq!(Class::IN, From::from(1u16));
445        assert_eq!(Class::CS, From::from(2u16));
446        assert_eq!(Class::CH, From::from(3u16));
447        assert_eq!(Class::HS, From::from(4u16));
448        assert_eq!(Class::Any, From::from(255u16));
449        assert_eq!(Class::Unknown(225u16), From::from(225u16));
450
451        assert_eq!(1u16, From::from(Class::IN));
452        assert_eq!(2u16, From::from(Class::CS));
453        assert_eq!(3u16, From::from(Class::CH));
454        assert_eq!(4u16, From::from(Class::HS));
455        assert_eq!(255u16, From::from(Class::Any));
456        assert_eq!(225u16, From::from(Class::Unknown(225u16)));
457    }
458
459    #[test]
460    fn qtype_transformations() {
461        assert_eq!(QType::A, From::from(1u16));
462        assert_eq!(QType::Ns, From::from(2u16));
463        assert_eq!(QType::Cname, From::from(5u16));
464        assert_eq!(QType::Mx, From::from(15u16));
465        assert_eq!(QType::All, From::from(255u16));
466        assert_eq!(QType::Unknown(225u16), From::from(225u16));
467
468        assert_eq!(1u16, From::from(QType::A));
469        assert_eq!(2u16, From::from(QType::Ns));
470        assert_eq!(5u16, From::from(QType::Cname));
471        assert_eq!(15u16, From::from(QType::Mx));
472        assert_eq!(255u16, From::from(QType::All));
473        assert_eq!(225u16, From::from(QType::Unknown(225u16)));
474    }
475}