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