dominion_parser/
header.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
5use crate::binutils::*;
6use crate::ParseError;
7
8macro_rules! u16_flag {
9    (
10        $(#[$outer:meta])*
11        $bits:literal is $typ:tt with: $(
12            #[$inner:meta]
13            $variant:tt = $value:literal
14        )+
15    ) => {
16        $(#[$outer])*
17        #[derive(Copy, Clone, Debug, PartialEq, Eq)]
18        pub enum $typ {
19            $(
20                #[$inner]
21                $variant = $value,
22            )*
23        }
24
25        impl From<u16> for $typ {
26            #[inline]
27            fn from(n: u16) -> Self {
28                match $crate::header::mask_shift($bits, n) {
29                    $($value => Self::$variant,)*
30                    _ => ::std::unreachable!("Bitwise operations should make this imposible. Failed with mask {} for value {}", $bits, n),
31                }
32            }
33        }
34
35        impl From<$typ> for u16 {
36            #[inline]
37            fn from(flag: $typ) -> Self {
38                $crate::header::unshift($bits, flag as u16)
39            }
40        }
41    };
42}
43
44macro_rules! u16_flag_reserved {
45    (
46        $(#[$outer:meta])*
47        $bits:literal is $typ:tt with: $(
48            #[$inner:meta]
49            $variant:tt = $value:literal
50        )+
51    ) => {
52        $(#[$outer])*
53        #[non_exhaustive]
54        #[derive(Copy, Clone, Debug, PartialEq, Eq)]
55        pub enum $typ {
56            $(
57                #[$inner]
58                $variant = $value,
59            )*
60        }
61
62        impl TryFrom<u16> for $typ {
63            type Error = ParseError;
64
65            #[inline]
66            fn try_from(n: u16) -> Result<Self, Self::Error> {
67                match $crate::header::mask_shift($bits, n) {
68                    $($value => Ok(Self::$variant),)*
69                    n => Err(ParseError::HeaderFlag(stringify!($typ), n)),
70                }
71            }
72        }
73
74        impl From<$typ> for u16 {
75            #[inline]
76            fn from(flag: $typ) -> Self {
77                $crate::header::unshift($bits, flag as u16)
78            }
79        }
80    };
81}
82
83#[inline]
84fn mask_shift(mask: u16, n: u16) -> u16 {
85    (n & mask) >> mask.trailing_zeros()
86}
87
88#[inline]
89fn unshift(mask: u16, n: u16) -> u16 {
90    n << mask.trailing_zeros()
91}
92
93/// A DNS header.
94///
95/// The header of a DNS packet follows the following structure:
96///
97/// ```text
98///       0  1  2  3  4  5  6  7  0  1  2  3  4  5  6  7
99///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
100///     |                      ID                       |
101///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
102///     |QR|   Opcode  |AA|TC|RD|RA| Z|AD|CD|   RCODE   |
103///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
104///     |                    QDCOUNT                    |
105///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
106///     |                    ANCOUNT                    |
107///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
108///     |                    NSCOUNT                    |
109///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
110///     |                    ARCOUNT                    |
111///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
112///
113/// ID: Random identifier of connnection
114/// QR: Query (0) or Response (1)
115/// OPCODE: Standard query (0), Inverse query (1), Server status query (2), Notify (4), Update (5), DSO (6)
116/// AA: Authoritative Answer
117/// TC: TrunCation
118/// RD: Recursion Desired
119/// RA: Recursion Available
120/// Z: Zero (reserved)
121/// AD: Authentic data (for DNSSEC)
122/// AD: Checking disabled (for DNSSEC)
123/// RCODE: Response code NOERROR (0), FORMERR (1), SERVFAIL (2), NXDOMAIN (3), NOTIMP (4), REFUSED (5)
124/// QDCOUNT: Question records count
125/// ANCOUNT: Answer records count
126/// NSCOUNT: Name server records count
127/// ARCOUNT: Aditional records count
128/// ```
129#[derive(Clone, Debug)]
130pub struct DnsHeader {
131    /// Random identifier of connnection
132    pub id: u16,
133    /// The different flags of a DNS header.
134    pub flags: Flags,
135    /// Question records count
136    pub questions: u16,
137    /// Answer records count
138    pub answers: u16,
139    /// Name server records count
140    pub authority: u16,
141    /// Aditional records count
142    pub additional: u16,
143}
144
145impl TryFrom<&[u8]> for DnsHeader {
146    type Error = crate::ParseError;
147    #[inline]
148    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
149        if bytes.len() < 12 {
150            Err(ParseError::HeaderLength(bytes.len()))?
151        } else {
152            let header = DnsHeader {
153                id: safe_u16_read(bytes, 0)?,
154                flags: safe_u16_read(bytes, 2)?.try_into()?,
155                questions: safe_u16_read(bytes, 4)?,
156                answers: safe_u16_read(bytes, 6)?,
157                authority: safe_u16_read(bytes, 8)?,
158                additional: safe_u16_read(bytes, 10)?,
159            };
160            Ok(header)
161        }
162    }
163}
164
165impl From<&DnsHeader> for Vec<u8> {
166    #[inline]
167    fn from(header: &DnsHeader) -> Self {
168        let mut target = Vec::with_capacity(12);
169        header.serialize(&mut target);
170        target
171    }
172}
173
174impl DnsHeader {
175    /// Serialize a [DnsHeader] into a vector of bytes.
176    ///
177    /// Usefult when you need to be able to apend the bytes to an existing `Vec<u8`,
178    /// in any other case the `From` trait is implemented to be able to convert from an
179    /// [DnsHeader] to an `Vec<u8>`.
180    #[inline]
181    pub fn serialize(&self, target: &mut Vec<u8>) {
182        push_u16(target, self.id);
183        push_u16(target, self.flags.into());
184        push_u16(target, self.questions);
185        push_u16(target, self.answers);
186        push_u16(target, self.authority);
187        push_u16(target, self.additional);
188    }
189}
190
191/// DNS Flags
192///
193/// ```text
194///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
195///     |QR|   Opcode  |AA|TC|RD|RA| Z|AD|CD|   RCODE   |
196///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
197/// QR: Query (0) or Response (1)
198/// OPCODE: Standard query (0), Inverse query (1), Server status query (2), Notify (4), Update (5), DSO (6)
199/// AA: Authoritative Answer
200/// TC: TrunCation
201/// RD: Recursion Desired
202/// RA: Recursion Available
203/// Z: Zero (reserved)
204/// AD: Authentic data (for DNSSEC)
205/// AD: Checking disabled (for DNSSEC)
206/// RCODE: Response code NOERROR (0), FORMERR (1), SERVFAIL (2), NXDOMAIN (3), NOTIMP (4), REFUSED (5)
207/// ```
208#[derive(Copy, Clone, Debug, PartialEq, Eq)]
209pub struct Flags {
210    /// Query (0) or Response (1)
211    pub qr: QueryResponse,
212    /// Standard query (0), Inverse query (1), Server status query (2), Notify (4), Update (5), DSO (6)
213    pub opcode: OpCode,
214    /// The answer is authoritative.
215    pub aa: AuthoritativeAnswer,
216    /// The packet has been truncated.
217    pub tc: TrunCation,
218    /// The client desires recursion.
219    pub rd: RecursionDesired,
220    /// The server has recursion availbale.
221    pub ra: RecursionAvailable,
222    /// Reserved (has to be 0).
223    pub z: Zero,
224    /// Authentic data (for DNSSEC)
225    pub ad: AuthenticData,
226    /// Checking disabled (for DNSSEC)
227    pub cd: CheckingDisabled,
228    /// Response code NOERROR (0), FORMERR (1), SERVFAIL (2), NXDOMAIN (3), NOTIMP (4), REFUSED (5)
229    pub rcode: ResponseCode,
230}
231
232impl TryFrom<u16> for Flags {
233    type Error = ParseError;
234
235    #[inline]
236    fn try_from(n: u16) -> Result<Self, Self::Error> {
237        Ok(Flags {
238            qr: n.into(),
239            opcode: n.try_into()?,
240            aa: n.into(),
241            tc: n.into(),
242            rd: n.into(),
243            ra: n.into(),
244            z: n.into(),
245            ad: n.into(),
246            cd: n.into(),
247            rcode: n.try_into()?,
248        })
249    }
250}
251
252impl From<Flags> for u16 {
253    #[inline]
254    fn from(flags: Flags) -> Self {
255        u16::from(flags.qr)
256            | u16::from(flags.opcode)
257            | u16::from(flags.aa)
258            | u16::from(flags.tc)
259            | u16::from(flags.rd)
260            | u16::from(flags.ra)
261            | u16::from(flags.z)
262            | u16::from(flags.ad)
263            | u16::from(flags.cd)
264            | u16::from(flags.rcode)
265    }
266}
267
268u16_flag! {
269    /// Query (0) or Response (1) packet.
270    0b1000000000000000 is QueryResponse with:
271        /// Query packet
272        Query = 0
273        /// Response packet
274        Response = 1
275}
276
277// TODO: Not exaustive. https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
278u16_flag_reserved! {
279    /// Standard query (0), Inverse query (1), Server status query (2), Notify (4), Update (5), DSO (6)
280    0b0111100000000000 is OpCode with:
281        /// Standard query
282        Query = 0
283        /// Inverse query
284        Iquery = 1
285        /// Server status query
286        Status = 2
287        /// Notify
288        Notify = 4
289        /// Update
290        Update = 5
291        /// DSO
292        Dso = 6
293}
294
295u16_flag! {
296    /// Flag to indicate if the answer is authoritative
297    0b0000010000000000 is AuthoritativeAnswer with:
298        /// The answer is not authoritative
299        NonAuthoritative = 0
300        /// The answer is authoritative
301        Authoritative = 1
302}
303
304u16_flag! {
305    /// Flag to indicate if the packet has been truncated.
306    0b0000001000000000 is TrunCation with:
307        /// The packet has not been truncated
308        NotTruncated = 0
309        /// The packet has been truncated
310        Truncated = 1
311}
312
313u16_flag! {
314    /// Flag to indicate if recursion is desired by the client.
315    0b0000000100000000 is RecursionDesired with:
316        /// Recursion is not desired
317        NotDesired = 0
318        /// Recursion is desired
319        Desired = 1
320}
321
322u16_flag! {
323    /// Flag to indicate if recursion is available by the server.
324    0b0000000010000000 is RecursionAvailable with:
325        /// Recursion is not available
326        NotAvailable = 0
327        /// Recursion is available
328        Available = 1
329}
330
331u16_flag! {
332    /// Reserved, should be 0.
333    0b0000000001000000 is Zero with:
334        /// Standard value
335        Zero = 0
336        /// Not used value.
337        Reserved = 1
338}
339
340u16_flag! {
341    /// DNSSEC flag to indicate if the data has been cryptographically authenticated
342    0b0000000000100000 is AuthenticData with:
343        /// The data is not cryptographically authenticated
344        NotAuthentic = 0
345        /// The data is cryptographically authenticated
346        Authentic = 1
347}
348
349u16_flag! {
350    /// DNSSEC flag to indicate if the client has enabled checking of the data.
351    0b0000000000010000 is CheckingDisabled with:
352        /// Checking has been enabled
353        Enabled = 0
354        /// Checking has been disabled
355        Disabled = 1
356}
357
358// TODO: Not exaustive. https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
359u16_flag_reserved! {
360    /// Response code
361    0b0000000000001111 is ResponseCode with:
362        /// There was no error.
363        NoError = 0
364        /// Format error - The name server was unable to interpret the query.
365        FormErr = 1
366        /// Server failure - The name server was unable to process this query due to a problem with the name server.
367        ServFail = 2
368        /// Name Error - Meaningful only for responses from an authoritative name server, this code signifies that the domain name referenced in the query does not exist.
369        NXDomain = 3
370        /// Not Implemented - The name server does not support the requested kind of query.
371        NotImp = 4
372        /// Refused - The name server refuses to perform the specified operation for policy reasons.  For example, a name server may not wish to provide the information to the particular requester, or a name server may not wish to perform a particular operation
373        Refused = 5
374}
375
376#[cfg(test)]
377mod tests {
378    use super::*;
379
380    #[test]
381    fn serialize_header() {
382        let mut used = Vec::with_capacity(12);
383        let initial = vec![
384            0x12u8, 0x34u8, 0u8, 0u8, 0u8, 1u8, 0u8, 2u8, 0u8, 3u8, 0u8, 4u8,
385        ];
386        let header = DnsHeader::try_from(&initial[..]).unwrap();
387        header.serialize(&mut used);
388        assert_eq!(initial, used);
389    }
390
391    #[test]
392    fn serialize_from_header() {
393        let initial = vec![
394            0x12u8, 0x34u8, 0u8, 0u8, 0u8, 1u8, 0u8, 2u8, 0u8, 3u8, 0u8, 4u8,
395        ];
396        let header = DnsHeader::try_from(&initial[..]).unwrap();
397        let used = Vec::<u8>::from(&header);
398        assert_eq!(initial, used);
399    }
400
401    #[test]
402    fn parse_header() {
403        let buff = [
404            0x12u8, 0x34u8, 0u8, 0u8, 0u8, 1u8, 0u8, 2u8, 0u8, 3u8, 0u8, 4u8,
405        ];
406        if let Ok(head) = DnsHeader::try_from(&buff[..]) {
407            assert_eq!(head.id, 0x1234u16);
408            assert_eq!(head.questions, 1u16);
409            assert_eq!(head.answers, 2u16);
410            assert_eq!(head.authority, 3u16);
411            assert_eq!(head.additional, 4u16);
412        } else {
413            panic!("Test should error with small buffer");
414        }
415    }
416
417    #[test]
418    fn header_err() {
419        let buff = [
420            0x12u8, 0x34u8, 0u8, 0u8, 0u8, 1u8, 0u8, 1u8, 0u8, 1u8, 0u8, 1u8,
421        ];
422        if DnsHeader::try_from(&buff[..5]).is_ok() {
423            panic!("Test should error with small buffer");
424        }
425    }
426
427    #[test]
428    fn flags_standard_query() {
429        let bits: u16 = 0b0000000000000000;
430        let flags: Flags = bits.try_into().expect("Failed when transforming flags");
431        let transformed: u16 = flags.into();
432
433        assert_eq!(flags.qr, QueryResponse::Query);
434        assert_eq!(flags.opcode, OpCode::Query);
435        assert_eq!(transformed, bits);
436    }
437
438    #[test]
439    fn flags_inverse_query() {
440        let bits: u16 = 0b0000100000000000;
441        let flags: Flags = bits.try_into().expect("Failed when transforming flags");
442        let transformed: u16 = flags.into();
443
444        assert_eq!(flags.qr, QueryResponse::Query);
445        assert_eq!(flags.opcode, OpCode::Iquery);
446        assert_eq!(transformed, bits);
447    }
448
449    #[test]
450    fn flags_response_noerror() {
451        let bits: u16 = 0b1000010000000000;
452        let flags: Flags = bits.try_into().expect("Failed when transforming flags");
453        let transformed: u16 = flags.into();
454
455        assert_eq!(flags.qr, QueryResponse::Response);
456        assert_eq!(flags.aa, AuthoritativeAnswer::Authoritative);
457        assert_eq!(flags.rcode, ResponseCode::NoError);
458        assert_eq!(transformed, bits);
459    }
460
461    #[test]
462    fn flags_response_servfail() {
463        let bits: u16 = 0b1000010000000010;
464        let flags: Flags = bits.try_into().expect("Failed when transforming flags");
465        let transformed: u16 = flags.into();
466
467        assert_eq!(flags.qr, QueryResponse::Response);
468        assert_eq!(flags.aa, AuthoritativeAnswer::Authoritative);
469        assert_eq!(flags.rcode, ResponseCode::ServFail);
470        assert_eq!(transformed, bits);
471    }
472
473    #[test]
474    fn flags_response_nxdomain() {
475        let bits: u16 = 0b1000010000000011;
476        let flags: Flags = bits.try_into().expect("Failed when transforming flags");
477        let transformed: u16 = flags.into();
478
479        assert_eq!(flags.qr, QueryResponse::Response);
480        assert_eq!(flags.aa, AuthoritativeAnswer::Authoritative);
481        assert_eq!(flags.rcode, ResponseCode::NXDomain);
482        assert_eq!(transformed, bits);
483    }
484
485    #[test]
486    fn flags_response_refused() {
487        let bits: u16 = 0b1000010000000101;
488        let flags: Flags = bits.try_into().expect("Failed when transforming flags");
489        let transformed: u16 = flags.into();
490
491        assert_eq!(flags.qr, QueryResponse::Response);
492        assert_eq!(flags.aa, AuthoritativeAnswer::Authoritative);
493        assert_eq!(flags.rcode, ResponseCode::Refused);
494        assert_eq!(transformed, bits);
495    }
496}