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}