Skip to main content

ddns/core/parser/
record.rs

1use std::{
2    fmt::Display,
3    io,
4    net::{Ipv4Addr, Ipv6Addr},
5};
6
7use bytes::Buf;
8use endpoint::{EndpointAddr, be_endpoint_addr_compat};
9use nom::{
10    Parser,
11    bytes::streaming::take,
12    combinator::map,
13    number::streaming::{be_u16, be_u32, be_u128},
14};
15use ptr::{Ptr, be_ptr};
16use srv::{Srv, be_srv};
17use txt::Txt;
18
19use super::name::{Name, be_name};
20
21pub mod endpoint;
22pub mod ptr;
23pub mod srv;
24pub mod txt;
25/// '''text
26/// 0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
27/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
28/// |                                               |
29/// /                                               /
30/// /                      NAME                     /
31/// |                                               |
32/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
33/// |                      TYPE                     |
34/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
35/// |                     CLASS                     |
36/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
37/// |                      TTL                      |
38/// |                                               |
39/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
40/// |                   RDLENGTH                    |
41/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
42/// /                     RDATA                     /
43/// /                                               /
44/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
45/// '''
46#[derive(Debug, PartialEq, Eq, Hash, Clone)]
47pub struct ResourceRecord {
48    pub(crate) name: Name,
49    pub(crate) typ: Type,
50    /// Whether or not the set of resource records is fully contained in the
51    /// packet, or whether there will be more resource records in future
52    /// packets. Only used for multicast DNS.
53    pub(crate) multicast_unique: bool,
54    pub(crate) cls: Class,
55    pub(crate) ttl: u32,
56    pub(crate) data: RData,
57}
58
59impl ResourceRecord {
60    pub fn data(&self) -> &RData {
61        &self.data
62    }
63
64    pub fn name(&self) -> Name {
65        self.name.clone()
66    }
67
68    pub fn typ(&self) -> Type {
69        self.typ
70    }
71
72    pub fn cls(&self) -> Class {
73        self.cls
74    }
75
76    pub fn ttl(&self) -> u32 {
77        self.ttl
78    }
79}
80
81/// The CLASS value according to RFC 1035
82#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
83pub enum Class {
84    /// the Internet
85    IN = 1,
86    /// the CSNET class (Obsolete - used only for examples in some obsolete
87    /// RFCs)
88    CS = 2,
89    /// the CHAOS class
90    CH = 3,
91    /// Hesiod [Dyer 87]
92    HS = 4,
93}
94
95impl TryFrom<u16> for Class {
96    type Error = io::Error;
97
98    fn try_from(value: u16) -> Result<Self, Self::Error> {
99        let value = value & 0x7FFF; // Mask to 15 bits
100        let class = match value {
101            1 => Self::IN,
102            2 => Self::CS,
103            3 => Self::CH,
104            4 => Self::HS,
105            _ => {
106                return Err(io::Error::new(
107                    io::ErrorKind::InvalidInput,
108                    format!("Unknown record class: {value}"),
109                ));
110            }
111        };
112        Ok(class)
113    }
114}
115
116impl From<Class> for u16 {
117    fn from(value: Class) -> Self {
118        match value {
119            Class::IN => 1,
120            Class::CS => 2,
121            Class::CH => 3,
122            Class::HS => 4,
123        }
124    }
125}
126
127/// The TYPE value according to RFC 1035
128///
129/// All "EXPERIMENTAL" markers here are from the RFC
130/// See <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml>
131#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
132pub enum Type {
133    /// 1 a host addresss
134    A,
135    /// 2 an authoritative name server
136    Ns,
137    /// 28 IPv6 host address (RFC 2782)
138    #[allow(clippy::upper_case_acronyms)]
139    AAAA,
140    /// 5 the canonical name for an alias
141    Cname,
142    /// 16 text strings
143    Txt,
144    /// 33 service record (RFC 2782)
145    Srv,
146    /// 12 a domain name pointer
147    Ptr,
148    /// 266 Unified endpoint address (IPv4/IPv6, direct/relay determined by flags)
149    E,
150}
151
152impl TryFrom<u16> for Type {
153    type Error = io::Error;
154
155    fn try_from(value: u16) -> Result<Self, Self::Error> {
156        let typ = match value {
157            1 => Self::A,
158            2 => Self::Ns,
159            28 => Self::AAAA,
160            5 => Self::Cname,
161            16 => Self::Txt,
162            33 => Self::Srv,
163            12 => Self::Ptr,
164            266 => Self::E,
165            // 保持向后兼容,将旧的类型映射到统一的 E 类型
166            267..=269 => Self::E,
167            _ => {
168                return Err(io::Error::new(
169                    io::ErrorKind::InvalidInput,
170                    format!("Unknown record type: {value}"),
171                ));
172            }
173        };
174        Ok(typ)
175    }
176}
177
178impl From<Type> for u16 {
179    fn from(value: Type) -> Self {
180        match value {
181            Type::A => 1,
182            Type::Ns => 2,
183            Type::AAAA => 28,
184            Type::Cname => 5,
185            Type::Txt => 16,
186            Type::Srv => 33,
187            Type::Ptr => 12,
188            Type::E => 266,
189        }
190    }
191}
192
193#[derive(Debug, PartialEq, Eq, Hash, Clone)]
194pub enum RData {
195    A(Ipv4Addr),
196    AAAA(Ipv6Addr),
197    CName(Name),
198    Txt(Txt),
199    Srv(Srv),
200    Ptr(Ptr),
201    E(EndpointAddr),
202}
203
204impl Display for RData {
205    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206        match self {
207            RData::A(ip) => write!(f, "{ip}"),
208            RData::AAAA(ip) => write!(f, "{ip}"),
209            RData::CName(name) => write!(f, "CName({name})"),
210            RData::Txt(txt) => write!(f, "{txt:?})"),
211            RData::Srv(srv) => write!(f, "{srv:?}"),
212            RData::Ptr(ptr) => write!(f, "{ptr:?}"),
213            RData::E(e) => write!(f, "{e}"),
214        }
215    }
216}
217
218pub fn be_record<'a>(input: &'a [u8], origin: &'a [u8]) -> nom::IResult<&'a [u8], ResourceRecord> {
219    let (remain, name) = be_name(input, origin)?;
220    let (remain, typ) = be_u16(remain)?;
221    let (remain, cls) = be_u16(remain)?;
222    let (remain, ttl) = be_u32(remain)?;
223    let (mut remain, rdlen) = be_u16(remain)?;
224
225    let Ok(typ) = Type::try_from(typ) else {
226        if remain.len() < rdlen as usize {
227            return Err(nom::Err::Incomplete(nom::Needed::new(
228                rdlen as usize - remain.len(),
229            )));
230        }
231        remain.advance(rdlen as usize);
232        return Err(nom::Err::Error(nom::error::make_error(
233            remain,
234            nom::error::ErrorKind::Alt,
235        )));
236    };
237
238    if remain.len() < rdlen as usize {
239        return Err(nom::Err::Incomplete(nom::Needed::new(
240            rdlen as usize - remain.len(),
241        )));
242    }
243    let (remain_after_rdata, rdata_bytes) = take(rdlen)(remain)?;
244    let (rdata_remain, rdata) = be_rdata(rdata_bytes, origin, typ, rdlen)?;
245    if !rdata_remain.is_empty() {
246        return Err(nom::Err::Error(nom::error::make_error(
247            remain_after_rdata,
248            nom::error::ErrorKind::Eof,
249        )));
250    }
251    let mut remain = remain_after_rdata;
252
253    let multicast_unique = cls & 0x8000 == 0x8000;
254    let cls = cls & 0x7FFF;
255    let Ok(cls) = Class::try_from(cls) else {
256        if remain.len() < rdlen as usize {
257            return Err(nom::Err::Incomplete(nom::Needed::new(
258                rdlen as usize - remain.len(),
259            )));
260        }
261        remain.advance(rdlen as usize);
262        return Err(nom::Err::Error(nom::error::make_error(
263            remain,
264            nom::error::ErrorKind::Alt,
265        )));
266    };
267
268    Ok((
269        remain,
270        ResourceRecord {
271            name,
272            typ,
273            multicast_unique,
274            cls,
275            ttl,
276            data: rdata,
277        },
278    ))
279}
280
281fn be_rdata<'a>(
282    input: &'a [u8],
283    origin: &'a [u8],
284    typ: Type,
285    rdlen: u16,
286) -> nom::IResult<&'a [u8], RData> {
287    match typ {
288        Type::A => map(be_u32, |ip| RData::A(Ipv4Addr::from(ip))).parse(input),
289        Type::AAAA => map(be_u128, |ip| RData::AAAA(Ipv6Addr::from(ip))).parse(input),
290        Type::Cname => be_name(input, origin).map(|(remain, name)| (remain, RData::CName(name))),
291        Type::Txt => map(take(rdlen), |txt: &[u8]| RData::Txt(Txt::new(txt.to_vec()))).parse(input),
292        Type::Srv => {
293            let (remain, srv) = be_srv(input, origin)?;
294            Ok((remain, RData::Srv(srv)))
295        }
296        Type::Ptr => be_ptr(input, origin).map(|(remain, ptr)| (remain, RData::Ptr(ptr))),
297        Type::Ns => be_name(input, origin).map(|(remain, name)| (remain, RData::CName(name))),
298        Type::E => be_endpoint_addr_compat(input, rdlen).map(|(remain, e)| (remain, RData::E(e))),
299    }
300}
301
302#[cfg(test)]
303mod test {
304    use super::*;
305
306    fn record_prefix_for_a(name: &[u8], rdlen: u16) -> Vec<u8> {
307        let mut buf = Vec::new();
308        buf.extend_from_slice(name);
309        buf.extend_from_slice(&1u16.to_be_bytes());
310        buf.extend_from_slice(&1u16.to_be_bytes());
311        buf.extend_from_slice(&0u32.to_be_bytes());
312        buf.extend_from_slice(&rdlen.to_be_bytes());
313        buf
314    }
315
316    #[test]
317    fn parse_record_incomplete_rdata_returns_incomplete() {
318        let name = b"\x07example\x03com\x00";
319        let mut buf = record_prefix_for_a(name, 4);
320        buf.extend_from_slice(&[127, 0, 0]);
321        let ret = be_record(&buf, &buf);
322        assert!(matches!(ret, Err(nom::Err::Incomplete(_))));
323    }
324
325    #[test]
326    fn parse_record_extra_rdata_bytes_is_error() {
327        let name = b"\x07example\x03com\x00";
328        let mut buf = record_prefix_for_a(name, 5);
329        buf.extend_from_slice(&[127, 0, 0, 1, 9]);
330        let ret = be_record(&buf, &buf);
331        assert!(matches!(ret, Err(nom::Err::Error(_))));
332    }
333}