1use nom::number::streaming::be_u16;
2
3use sawp::error::Result;
4
5use sawp_flags::{BitFlags, Flag, Flags};
6
7use crate::enums::{OpCode, QueryResponse, ResponseCode};
8
9use crate::ErrorFlags;
10#[cfg(feature = "ffi")]
11use sawp_ffi::GenerateFFI;
12
13#[allow(non_camel_case_types)]
15#[derive(Debug, Clone, Copy, PartialEq, Eq, BitFlags)]
16#[repr(u16)]
17pub enum header_masks {
18 QUERY_RESPONSE = 0b1000_0000_0000_0000,
19 OPCODE = 0b0111_1000_0000_0000,
20 AUTH = 0b0000_0100_0000_0000,
21 TRUNC = 0b0000_0010_0000_0000,
22 RECUR_DESIRED = 0b0000_0001_0000_0000,
23 RECUR_AVAIL = 0b0000_0000_1000_0000,
24 Z = 0b0000_0000_0100_0000,
25 AUTH_DATA = 0b0000_0000_0010_0000,
26 CHECK_DISABLED = 0b0000_0000_0001_0000,
27 RCODE = 0b0000_0000_0000_1111,
28}
29
30#[cfg_attr(feature = "ffi", derive(GenerateFFI))]
32#[cfg_attr(feature = "ffi", sawp_ffi(prefix = "sawp_dns"))]
33#[derive(Debug, PartialEq, Eq)]
34pub struct Header {
35 pub transaction_id: u16,
37 pub flags: u16,
39 #[cfg_attr(feature = "ffi", sawp_ffi(copy))]
40 pub query_response: QueryResponse,
42 #[cfg_attr(feature = "ffi", sawp_ffi(copy))]
43 pub opcode: OpCode,
45 pub authoritative: bool,
47 pub truncated: bool,
49 pub recursion_desired: bool,
51 pub recursion_available: bool,
53 pub zflag: bool,
55
56 pub authenticated_data: bool,
58
59 pub check_disabled: bool,
60 #[cfg_attr(feature = "ffi", sawp_ffi(copy))]
61 pub rcode: ResponseCode,
63 pub qdcount: u16,
65 pub ancount: u16,
67 pub nscount: u16,
69 pub arcount: u16,
71}
72
73impl Header {
74 #[allow(clippy::type_complexity)]
75 pub fn parse(input: &[u8]) -> Result<(&[u8], (Header, Flags<ErrorFlags>))> {
76 let mut error_flags = ErrorFlags::none();
77
78 let (input, txid) = be_u16(input)?;
79 let (input, flags) = be_u16(input)?;
80 let wrapped_flags = Flags::<header_masks>::from_bits(flags);
81 let query = if wrapped_flags.intersects(header_masks::QUERY_RESPONSE) {
82 QueryResponse::Response
83 } else {
84 QueryResponse::Query
85 };
86 let opcode: OpCode = OpCode::from_raw((wrapped_flags & header_masks::OPCODE).bits() >> 10);
87 if opcode == OpCode::UNKNOWN {
88 error_flags |= ErrorFlags::UnknownOpcode;
89 }
90 let rcode: ResponseCode =
91 ResponseCode::from_raw((wrapped_flags & header_masks::RCODE).bits());
92 if rcode == ResponseCode::UNKNOWN {
93 error_flags |= ErrorFlags::UnknownRcode;
94 }
95 let (input, qcnt) = be_u16(input)?;
96 let (input, acnt) = be_u16(input)?;
97 let (input, nscnt) = be_u16(input)?;
98 let (input, arcnt) = be_u16(input)?;
99
100 Ok((
101 input,
102 (
103 Header {
104 transaction_id: txid,
105 flags,
106 query_response: query,
107 opcode,
108 authoritative: wrapped_flags.intersects(header_masks::AUTH),
109 truncated: wrapped_flags.intersects(header_masks::TRUNC),
110 recursion_desired: wrapped_flags.intersects(header_masks::RECUR_DESIRED),
111 recursion_available: wrapped_flags.intersects(header_masks::RECUR_AVAIL),
112 zflag: wrapped_flags.intersects(header_masks::Z),
113 authenticated_data: wrapped_flags.intersects(header_masks::AUTH_DATA),
114 check_disabled: wrapped_flags.intersects(header_masks::CHECK_DISABLED),
115 rcode,
116 qdcount: qcnt,
117 ancount: acnt,
118 nscount: nscnt,
119 arcount: arcnt,
120 },
121 error_flags,
122 ),
123 ))
124 }
125}
126
127#[cfg(test)]
128mod test {
129 #![allow(clippy::type_complexity)]
130
131 use crate::{ErrorFlags, Header, OpCode, QueryResponse, ResponseCode};
132 use rstest::rstest;
133 use sawp::error::{Error, Result};
134 use sawp_flags::{Flag, Flags};
135
136 #[rstest(
137 input,
138 expected,
139 case::parse_simple_header(
140 & [
141 0x31, 0x21, 0x81, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, ],
148 Ok((
149 b"".as_ref(),
150 (Header {
151 transaction_id: 0x3121,
152 flags: 0b1000_0001_0000_0000,
153 query_response: QueryResponse::Response,
154 opcode: OpCode::QUERY,
155 authoritative: false,
156 truncated: false,
157 recursion_desired: true,
158 recursion_available: false,
159 zflag: false,
160 authenticated_data: false,
161 check_disabled: false,
162 rcode: ResponseCode::NOERROR,
163 qdcount: 1,
164 ancount: 1,
165 nscount: 0,
166 arcount: 0,
167 },
168 ErrorFlags::none())
169 ))
170 ),
171 case::parse_too_short_header(
172 & [
173 0x31, 0x21, 0x81, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, ],
179 Err(Error::incomplete_needed(2))
180 ),
181 case::parse_header_bad_opcode(
182 & [
183 0x31, 0x21, 0xb1, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, ],
190 Ok((
191 b"".as_ref(),
192 (Header {
193 transaction_id: 0x3121,
194 flags: 0b1011_0001_0000_0000,
195 query_response: QueryResponse::Response,
196 opcode: OpCode::UNKNOWN,
197 authoritative: false,
198 truncated: false,
199 recursion_desired: true,
200 recursion_available: false,
201 zflag: false,
202 authenticated_data: false,
203 check_disabled: false,
204 rcode: ResponseCode::NOERROR,
205 qdcount: 1,
206 ancount: 1,
207 nscount: 0,
208 arcount: 0,
209 },
210 ErrorFlags::UnknownOpcode.into())
211 ))
212 ),
213 case::parse_header_bad_rcode(
214 & [
215 0x31, 0x21, 0x81, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, ],
222 Ok((
223 b"".as_ref(),
224 (Header {
225 transaction_id: 0x3121,
226 flags: 0b1000_0001_0000_1100,
227 query_response: QueryResponse::Response,
228 opcode: OpCode::QUERY,
229 authoritative: false,
230 truncated: false,
231 recursion_desired: true,
232 recursion_available: false,
233 zflag: false,
234 authenticated_data: false,
235 check_disabled: false,
236 rcode: ResponseCode::UNKNOWN,
237 qdcount: 1,
238 ancount: 1,
239 nscount: 0,
240 arcount: 0,
241 },
242 ErrorFlags::UnknownRcode.into())
243 ))
244 ),
245 )]
246 fn header(input: &[u8], expected: Result<(&[u8], (Header, Flags<ErrorFlags>))>) {
247 assert_eq!(Header::parse(input), expected);
248 }
249}