Skip to main content

stackforge_core/layer/dns/
mod.rs

1//! DNS protocol layer implementation (RFC 1035).
2//!
3//! Provides full DNS parsing, building, and field access with:
4//! - Header flag fields (QR, opcode, AA, TC, RD, RA, Z, AD, CD, RCODE)
5//! - Question section with name compression
6//! - Resource record sections (answer, authority, additional)
7//! - All major RR types (A, AAAA, NS, CNAME, SOA, MX, TXT, SRV, etc.)
8//! - DNSSEC types (RRSIG, NSEC, NSEC3, DNSKEY, DS)
9//! - EDNS0 OPT records
10//! - SVCB/HTTPS service binding records
11//! - DNS name compression and decompression
12
13pub mod bitmap;
14pub mod builder;
15pub mod edns;
16pub mod header;
17pub mod query;
18pub mod rdata;
19pub mod rr;
20pub mod svcb;
21pub mod types;
22
23pub use builder::DnsBuilder;
24pub use query::DnsQuestion;
25pub use rdata::DnsRData;
26pub use rr::DnsResourceRecord;
27
28use crate::layer::field::{FieldError, FieldValue};
29use crate::layer::{Layer, LayerIndex, LayerKind};
30
31/// DNS header length in bytes.
32pub const DNS_HEADER_LEN: usize = header::DNS_HEADER_LEN;
33
34/// DNS layer — a zero-copy view into the packet buffer.
35///
36/// Provides lazy field access for all DNS header fields and
37/// on-demand parsing of question/RR sections.
38#[derive(Debug, Clone)]
39pub struct DnsLayer {
40    pub index: LayerIndex,
41}
42
43impl DnsLayer {
44    /// Create a new `DnsLayer` from start/end offsets.
45    #[must_use]
46    pub fn new(start: usize, end: usize) -> Self {
47        Self {
48            index: LayerIndex::new(LayerKind::Dns, start, end),
49        }
50    }
51
52    // ========================================================================
53    // Header field accessors
54    // ========================================================================
55
56    /// Transaction ID.
57    pub fn id(&self, buf: &[u8]) -> Result<u16, FieldError> {
58        header::read_id(buf, self.index.start)
59    }
60
61    /// QR flag: false=query, true=response.
62    pub fn qr(&self, buf: &[u8]) -> Result<bool, FieldError> {
63        header::read_qr(buf, self.index.start)
64    }
65
66    /// Operation code (4 bits).
67    pub fn opcode(&self, buf: &[u8]) -> Result<u8, FieldError> {
68        header::read_opcode(buf, self.index.start)
69    }
70
71    /// Authoritative Answer flag.
72    pub fn aa(&self, buf: &[u8]) -> Result<bool, FieldError> {
73        header::read_aa(buf, self.index.start)
74    }
75
76    /// Truncation flag.
77    pub fn tc(&self, buf: &[u8]) -> Result<bool, FieldError> {
78        header::read_tc(buf, self.index.start)
79    }
80
81    /// Recursion Desired flag.
82    pub fn rd(&self, buf: &[u8]) -> Result<bool, FieldError> {
83        header::read_rd(buf, self.index.start)
84    }
85
86    /// Recursion Available flag.
87    pub fn ra(&self, buf: &[u8]) -> Result<bool, FieldError> {
88        header::read_ra(buf, self.index.start)
89    }
90
91    /// Reserved flag (Z).
92    pub fn z(&self, buf: &[u8]) -> Result<bool, FieldError> {
93        header::read_z(buf, self.index.start)
94    }
95
96    /// Authenticated Data flag (DNSSEC).
97    pub fn ad(&self, buf: &[u8]) -> Result<bool, FieldError> {
98        header::read_ad(buf, self.index.start)
99    }
100
101    /// Checking Disabled flag (DNSSEC).
102    pub fn cd(&self, buf: &[u8]) -> Result<bool, FieldError> {
103        header::read_cd(buf, self.index.start)
104    }
105
106    /// Response code (4 bits).
107    pub fn rcode(&self, buf: &[u8]) -> Result<u8, FieldError> {
108        header::read_rcode(buf, self.index.start)
109    }
110
111    /// Question count.
112    pub fn qdcount(&self, buf: &[u8]) -> Result<u16, FieldError> {
113        header::read_qdcount(buf, self.index.start)
114    }
115
116    /// Answer count.
117    pub fn ancount(&self, buf: &[u8]) -> Result<u16, FieldError> {
118        header::read_ancount(buf, self.index.start)
119    }
120
121    /// Authority count.
122    pub fn nscount(&self, buf: &[u8]) -> Result<u16, FieldError> {
123        header::read_nscount(buf, self.index.start)
124    }
125
126    /// Additional count.
127    pub fn arcount(&self, buf: &[u8]) -> Result<u16, FieldError> {
128        header::read_arcount(buf, self.index.start)
129    }
130
131    // ========================================================================
132    // Section parsing
133    // ========================================================================
134
135    /// Parse all questions from the packet.
136    pub fn questions(&self, buf: &[u8]) -> Result<Vec<DnsQuestion>, FieldError> {
137        let qdcount = self.qdcount(buf)? as usize;
138        let mut questions = Vec::with_capacity(qdcount);
139        let dns_data = &buf[self.index.start..self.index.end];
140        let mut offset = DNS_HEADER_LEN;
141
142        for _ in 0..qdcount {
143            let (q, consumed) = DnsQuestion::parse(dns_data, offset)?;
144            questions.push(q);
145            offset += consumed;
146        }
147
148        Ok(questions)
149    }
150
151    /// Parse all answer RRs from the packet.
152    pub fn answers_rr(&self, buf: &[u8]) -> Result<Vec<DnsResourceRecord>, FieldError> {
153        let qdcount = self.qdcount(buf)? as usize;
154        let ancount = self.ancount(buf)? as usize;
155        let dns_data = &buf[self.index.start..self.index.end];
156
157        // Skip past header and questions
158        let mut offset = DNS_HEADER_LEN;
159        for _ in 0..qdcount {
160            let (_, consumed) = DnsQuestion::parse(dns_data, offset)?;
161            offset += consumed;
162        }
163
164        // Parse answer records
165        let mut records = Vec::with_capacity(ancount);
166        for _ in 0..ancount {
167            let (rr, consumed) = DnsResourceRecord::parse(dns_data, offset)?;
168            records.push(rr);
169            offset += consumed;
170        }
171
172        Ok(records)
173    }
174
175    /// Parse all authority RRs from the packet.
176    pub fn authorities(&self, buf: &[u8]) -> Result<Vec<DnsResourceRecord>, FieldError> {
177        let qdcount = self.qdcount(buf)? as usize;
178        let ancount = self.ancount(buf)? as usize;
179        let nscount = self.nscount(buf)? as usize;
180        let dns_data = &buf[self.index.start..self.index.end];
181
182        // Skip past header, questions, and answers
183        let mut offset = DNS_HEADER_LEN;
184        for _ in 0..qdcount {
185            let (_, consumed) = DnsQuestion::parse(dns_data, offset)?;
186            offset += consumed;
187        }
188        for _ in 0..ancount {
189            let (_, consumed) = DnsResourceRecord::parse(dns_data, offset)?;
190            offset += consumed;
191        }
192
193        // Parse authority records
194        let mut records = Vec::with_capacity(nscount);
195        for _ in 0..nscount {
196            let (rr, consumed) = DnsResourceRecord::parse(dns_data, offset)?;
197            records.push(rr);
198            offset += consumed;
199        }
200
201        Ok(records)
202    }
203
204    /// Parse all additional RRs from the packet.
205    pub fn additionals(&self, buf: &[u8]) -> Result<Vec<DnsResourceRecord>, FieldError> {
206        let qdcount = self.qdcount(buf)? as usize;
207        let ancount = self.ancount(buf)? as usize;
208        let nscount = self.nscount(buf)? as usize;
209        let arcount = self.arcount(buf)? as usize;
210        let dns_data = &buf[self.index.start..self.index.end];
211
212        // Skip past header, questions, answers, and authority
213        let mut offset = DNS_HEADER_LEN;
214        for _ in 0..qdcount {
215            let (_, consumed) = DnsQuestion::parse(dns_data, offset)?;
216            offset += consumed;
217        }
218        for _ in 0..ancount {
219            let (_, consumed) = DnsResourceRecord::parse(dns_data, offset)?;
220            offset += consumed;
221        }
222        for _ in 0..nscount {
223            let (_, consumed) = DnsResourceRecord::parse(dns_data, offset)?;
224            offset += consumed;
225        }
226
227        // Parse additional records
228        let mut records = Vec::with_capacity(arcount);
229        for _ in 0..arcount {
230            let (rr, consumed) = DnsResourceRecord::parse(dns_data, offset)?;
231            records.push(rr);
232            offset += consumed;
233        }
234
235        Ok(records)
236    }
237
238    /// Parse all sections at once.
239    pub fn parse_all(
240        &self,
241        buf: &[u8],
242    ) -> Result<
243        (
244            Vec<DnsQuestion>,
245            Vec<DnsResourceRecord>,
246            Vec<DnsResourceRecord>,
247            Vec<DnsResourceRecord>,
248        ),
249        FieldError,
250    > {
251        let qdcount = self.qdcount(buf)? as usize;
252        let ancount = self.ancount(buf)? as usize;
253        let nscount = self.nscount(buf)? as usize;
254        let arcount = self.arcount(buf)? as usize;
255        let dns_data = &buf[self.index.start..self.index.end];
256        let mut offset = DNS_HEADER_LEN;
257
258        let mut questions = Vec::with_capacity(qdcount);
259        for _ in 0..qdcount {
260            let (q, consumed) = DnsQuestion::parse(dns_data, offset)?;
261            questions.push(q);
262            offset += consumed;
263        }
264
265        let mut answers = Vec::with_capacity(ancount);
266        for _ in 0..ancount {
267            let (rr, consumed) = DnsResourceRecord::parse(dns_data, offset)?;
268            answers.push(rr);
269            offset += consumed;
270        }
271
272        let mut authorities = Vec::with_capacity(nscount);
273        for _ in 0..nscount {
274            let (rr, consumed) = DnsResourceRecord::parse(dns_data, offset)?;
275            authorities.push(rr);
276            offset += consumed;
277        }
278
279        let mut additionals = Vec::with_capacity(arcount);
280        for _ in 0..arcount {
281            let (rr, consumed) = DnsResourceRecord::parse(dns_data, offset)?;
282            additionals.push(rr);
283            offset += consumed;
284        }
285
286        Ok((questions, answers, authorities, additionals))
287    }
288
289    // ========================================================================
290    // Field access (dynamic)
291    // ========================================================================
292
293    /// Get a field value by name.
294    pub fn get_field(&self, buf: &[u8], name: &str) -> Option<Result<FieldValue, FieldError>> {
295        match name {
296            "id" => Some(self.id(buf).map(FieldValue::U16)),
297            "qr" => Some(self.qr(buf).map(FieldValue::Bool)),
298            "opcode" => Some(self.opcode(buf).map(FieldValue::U8)),
299            "aa" => Some(self.aa(buf).map(FieldValue::Bool)),
300            "tc" => Some(self.tc(buf).map(FieldValue::Bool)),
301            "rd" => Some(self.rd(buf).map(FieldValue::Bool)),
302            "ra" => Some(self.ra(buf).map(FieldValue::Bool)),
303            "z" => Some(self.z(buf).map(FieldValue::Bool)),
304            "ad" => Some(self.ad(buf).map(FieldValue::Bool)),
305            "cd" => Some(self.cd(buf).map(FieldValue::Bool)),
306            "rcode" => Some(self.rcode(buf).map(FieldValue::U8)),
307            "qdcount" => Some(self.qdcount(buf).map(FieldValue::U16)),
308            "ancount" => Some(self.ancount(buf).map(FieldValue::U16)),
309            "nscount" => Some(self.nscount(buf).map(FieldValue::U16)),
310            "arcount" => Some(self.arcount(buf).map(FieldValue::U16)),
311            _ => None,
312        }
313    }
314
315    /// Set a field value by name.
316    pub fn set_field(
317        &self,
318        buf: &mut [u8],
319        name: &str,
320        value: FieldValue,
321    ) -> Option<Result<(), FieldError>> {
322        match name {
323            "id" => Some(match value.as_u16() {
324                Some(v) => header::write_id(buf, self.index.start, v),
325                None => Err(FieldError::InvalidValue("expected u16 for id".into())),
326            }),
327            "qr" => Some(match value.as_bool() {
328                Some(v) => header::write_qr(buf, self.index.start, v),
329                None => Err(FieldError::InvalidValue("expected bool for qr".into())),
330            }),
331            "opcode" => Some(match value.as_u8() {
332                Some(v) => header::write_opcode(buf, self.index.start, v),
333                None => Err(FieldError::InvalidValue("expected u8 for opcode".into())),
334            }),
335            "aa" => Some(match value.as_bool() {
336                Some(v) => header::write_aa(buf, self.index.start, v),
337                None => Err(FieldError::InvalidValue("expected bool for aa".into())),
338            }),
339            "tc" => Some(match value.as_bool() {
340                Some(v) => header::write_tc(buf, self.index.start, v),
341                None => Err(FieldError::InvalidValue("expected bool for tc".into())),
342            }),
343            "rd" => Some(match value.as_bool() {
344                Some(v) => header::write_rd(buf, self.index.start, v),
345                None => Err(FieldError::InvalidValue("expected bool for rd".into())),
346            }),
347            "ra" => Some(match value.as_bool() {
348                Some(v) => header::write_ra(buf, self.index.start, v),
349                None => Err(FieldError::InvalidValue("expected bool for ra".into())),
350            }),
351            "z" => Some(match value.as_bool() {
352                Some(v) => header::write_z(buf, self.index.start, v),
353                None => Err(FieldError::InvalidValue("expected bool for z".into())),
354            }),
355            "ad" => Some(match value.as_bool() {
356                Some(v) => header::write_ad(buf, self.index.start, v),
357                None => Err(FieldError::InvalidValue("expected bool for ad".into())),
358            }),
359            "cd" => Some(match value.as_bool() {
360                Some(v) => header::write_cd(buf, self.index.start, v),
361                None => Err(FieldError::InvalidValue("expected bool for cd".into())),
362            }),
363            "rcode" => Some(match value.as_u8() {
364                Some(v) => header::write_rcode(buf, self.index.start, v),
365                None => Err(FieldError::InvalidValue("expected u8 for rcode".into())),
366            }),
367            "qdcount" => Some(match value.as_u16() {
368                Some(v) => header::write_qdcount(buf, self.index.start, v),
369                None => Err(FieldError::InvalidValue("expected u16 for qdcount".into())),
370            }),
371            "ancount" => Some(match value.as_u16() {
372                Some(v) => header::write_ancount(buf, self.index.start, v),
373                None => Err(FieldError::InvalidValue("expected u16 for ancount".into())),
374            }),
375            "nscount" => Some(match value.as_u16() {
376                Some(v) => header::write_nscount(buf, self.index.start, v),
377                None => Err(FieldError::InvalidValue("expected u16 for nscount".into())),
378            }),
379            "arcount" => Some(match value.as_u16() {
380                Some(v) => header::write_arcount(buf, self.index.start, v),
381                None => Err(FieldError::InvalidValue("expected u16 for arcount".into())),
382            }),
383            _ => None,
384        }
385    }
386
387    /// Get the list of field names.
388    #[must_use]
389    pub fn field_names() -> &'static [&'static str] {
390        DNS_FIELDS
391    }
392}
393
394/// DNS field names for dynamic access.
395pub const DNS_FIELDS: &[&str] = &[
396    "id", "qr", "opcode", "aa", "tc", "rd", "ra", "z", "ad", "cd", "rcode", "qdcount", "ancount",
397    "nscount", "arcount",
398];
399
400impl Layer for DnsLayer {
401    fn kind(&self) -> LayerKind {
402        LayerKind::Dns
403    }
404
405    fn summary(&self, buf: &[u8]) -> String {
406        let qr = self.qr(buf).unwrap_or(false);
407        let opcode = self.opcode(buf).unwrap_or(0);
408        let rcode = self.rcode(buf).unwrap_or(0);
409        let qdcount = self.qdcount(buf).unwrap_or(0);
410        let ancount = self.ancount(buf).unwrap_or(0);
411
412        if qr {
413            format!(
414                "DNS {} {} qd={} an={}",
415                types::opcode_name(opcode),
416                types::rcode_name(rcode),
417                qdcount,
418                ancount,
419            )
420        } else {
421            let qname_str = self
422                .questions(buf)
423                .ok()
424                .and_then(|qs| qs.first().map(query::DnsQuestion::summary));
425            match qname_str {
426                Some(q) => format!("DNS {} {}", types::opcode_name(opcode), q),
427                None => format!("DNS {} qd={}", types::opcode_name(opcode), qdcount),
428            }
429        }
430    }
431
432    fn header_len(&self, buf: &[u8]) -> usize {
433        self.index
434            .len()
435            .min(buf.len().saturating_sub(self.index.start))
436    }
437
438    fn hashret(&self, buf: &[u8]) -> Vec<u8> {
439        self.id(buf)
440            .map(|id| id.to_be_bytes().to_vec())
441            .unwrap_or_default()
442    }
443
444    fn field_names(&self) -> &'static [&'static str] {
445        DNS_FIELDS
446    }
447}