dns_server/
dns_message_header.rs

1use crate::{read_u16_be, read_u8, write_u16_be, DnsError, DnsOpCode, DnsResponseCode};
2use fixed_buffer::FixedBuf;
3
4/// > 4.1.1. Header section format
5/// >
6/// > The header contains the following fields:
7/// >
8/// > ```text
9/// >                                 1  1  1  1  1  1
10/// >   0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
11/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
12/// > |                      ID                       |
13/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
14/// > |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
15/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
16/// > |                    QDCOUNT                    |
17/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
18/// > |                    ANCOUNT                    |
19/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
20/// > |                    NSCOUNT                    |
21/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
22/// > |                    ARCOUNT                    |
23/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
24/// > ```
25///
26/// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
27#[allow(clippy::struct_excessive_bools)]
28#[derive(Clone, Debug, Eq, Hash, PartialEq)]
29pub struct DnsMessageHeader {
30    /// > `ID` A 16 bit identifier assigned by the program that generates any kind of query.  This
31    /// > identifier is copied the corresponding reply and can be used by the requester to match up
32    /// > replies to outstanding queries.
33    ///
34    /// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
35    pub id: u16,
36    /// > `QR` A one bit field that specifies whether this message is a query (`0`),
37    /// > or a response (`1`).
38    ///
39    /// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
40    pub is_response: bool,
41    /// > `OPCODE`  A four bit field that specifies kind of query in this message.
42    /// >         This value is set by the originator of a query and copied into
43    /// >         the response.  The values are:
44    /// > - `0` a standard query (`QUERY`)
45    /// > - `1` an inverse query (`IQUERY`)
46    /// > - `2` a server status request (`STATUS`)
47    /// > - `3-15` reserved for future use
48    ///
49    /// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
50    pub op_code: DnsOpCode,
51    /// > `AA` Authoritative Answer - this bit is valid in responses, and specifies that the
52    /// > responding name server is an authority for the domain name in question section.
53    /// >
54    /// > Note that the contents of the answer section may have multiple owner names because of
55    /// > aliases.  The AA bit corresponds to the name which matches the query name, or the first
56    /// > owner name in the answer section.
57    ///
58    /// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
59    pub authoritative_answer: bool,
60    /// > `TC` TrunCation - specifies that this message was truncated due to length greater than
61    /// > that permitted on the transmission channel.
62    ///
63    /// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
64    pub truncated: bool,
65    /// > `RD` Recursion Desired - this bit may be set in a query and is copied into the response.
66    /// > If RD is set, it directs the name server to pursue the query recursively.  Recursive query
67    /// > support is optional.
68    ///
69    /// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
70    pub recursion_desired: bool,
71    /// > `RA` Recursion Available - this be is set or cleared in a response, and denotes whether
72    /// > recursive query support is available in the name server.
73    ///
74    /// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
75    pub recursion_available: bool,
76    pub response_code: DnsResponseCode,
77    pub question_count: u16,
78    pub answer_count: u16,
79    pub name_server_count: u16,
80    pub additional_count: u16,
81}
82impl DnsMessageHeader {
83    /// # Errors
84    /// Returns an error when `buf` does not contain a valid message header.
85    pub fn read<const N: usize>(buf: &mut FixedBuf<N>) -> Result<Self, DnsError> {
86        let id = read_u16_be(buf)?;
87        let b = read_u8(buf)?;
88        let is_response = (b >> 7) == 1;
89        let op_code = DnsOpCode::new((b >> 3) & 0xF);
90        let authoritative_answer = ((b >> 2) & 1) == 1;
91        let truncated = ((b >> 1) & 1) == 1;
92        let recursion_desired = (b & 1) == 1;
93        let b = read_u8(buf)?;
94        let recursion_available = (b >> 7) == 1;
95        let response_code = DnsResponseCode::new(b & 0xF);
96        let question_count = read_u16_be(buf)?;
97        let answer_count = read_u16_be(buf)?;
98        let name_server_count = read_u16_be(buf)?;
99        let additional_count = read_u16_be(buf)?;
100        Ok(Self {
101            id,
102            is_response,
103            op_code,
104            authoritative_answer,
105            truncated,
106            recursion_desired,
107            recursion_available,
108            response_code,
109            question_count,
110            answer_count,
111            name_server_count,
112            additional_count,
113        })
114    }
115
116    /// # Errors
117    /// Returns an error when `buf` fills up.
118    pub fn write<const N: usize>(&self, out: &mut FixedBuf<N>) -> Result<(), DnsError> {
119        let bytes: [u8; 2] = self.id.to_be_bytes();
120        out.write_bytes(&bytes)
121            .map_err(|_| DnsError::ResponseBufferFull)?;
122        let b = (u8::from(self.is_response) << 7)
123            | (self.op_code.num() << 3)
124            | (u8::from(self.authoritative_answer) << 2)
125            | (u8::from(self.truncated) << 1)
126            | u8::from(self.recursion_desired);
127        out.write_bytes(&[b])
128            .map_err(|_| DnsError::ResponseBufferFull)?;
129        let b = (u8::from(self.recursion_available) << 7) | self.response_code.num();
130        out.write_bytes(&[b])
131            .map_err(|_| DnsError::ResponseBufferFull)?;
132        for count in [
133            self.question_count,
134            self.answer_count,
135            self.name_server_count,
136            self.additional_count,
137        ] {
138            write_u16_be(out, count)?;
139        }
140        Ok(())
141    }
142}