simple_dns/dns/rdata/
naptr.rs

1use crate::{
2    bytes_buffer::BytesBuffer,
3    dns::{CharacterString, Name, WireFormat},
4};
5
6use super::RR;
7
8/// RFC 3403: Used to map a domain name to a set of services. The fields determine
9///           the order of processing, specify the protocol and service to be used,
10///           and transform the original domain name into a new domain name or URI.
11
12#[derive(Debug, PartialEq, Eq, Hash, Clone)]
13pub struct NAPTR<'a> {
14    /// Order in which NAPTR records must be processed
15    pub order: u16,
16    /// Order in which NAPTR records with equal Order values should be processed
17    pub preference: u16,
18    /// Control rewriting and interpretation of the fields in the record
19    pub flags: CharacterString<'a>,
20    /// Service Parameters applicable to this this delegation path
21    pub services: CharacterString<'a>,
22    /// Regular expression applied to original string from client
23    pub regexp: CharacterString<'a>,
24    /// Next domain-name to query for
25    pub replacement: Name<'a>,
26}
27
28impl RR for NAPTR<'_> {
29    const TYPE_CODE: u16 = 35;
30}
31
32impl NAPTR<'_> {
33    /// Transforms the inner data into it owned type
34    pub fn into_owned<'b>(self) -> NAPTR<'b> {
35        NAPTR {
36            order: self.order,
37            preference: self.preference,
38            flags: self.flags.into_owned(),
39            services: self.services.into_owned(),
40            regexp: self.regexp.into_owned(),
41            replacement: self.replacement.into_owned(),
42        }
43    }
44}
45
46impl<'a> WireFormat<'a> for NAPTR<'a> {
47    const MINIMUM_LEN: usize = 4;
48
49    fn parse(data: &mut BytesBuffer<'a>) -> crate::Result<Self>
50    where
51        Self: Sized,
52    {
53        let order = data.get_u16()?;
54        let preference = data.get_u16()?;
55        let flags = CharacterString::parse(data)?;
56        let services = CharacterString::parse(data)?;
57        let regexp = CharacterString::parse(data)?;
58        let replacement = Name::parse(data)?;
59
60        Ok(Self {
61            order,
62            preference,
63            flags,
64            services,
65            regexp,
66            replacement,
67        })
68    }
69
70    fn write_to<T: std::io::Write>(&self, out: &mut T) -> crate::Result<()> {
71        out.write_all(&self.order.to_be_bytes())?;
72        out.write_all(&self.preference.to_be_bytes())?;
73        self.flags.write_to(out)?;
74        self.services.write_to(out)?;
75        self.regexp.write_to(out)?;
76        self.replacement.write_to(out)
77    }
78
79    fn len(&self) -> usize {
80        self.flags.len()
81            + self.services.len()
82            + self.regexp.len()
83            + self.replacement.len()
84            + Self::MINIMUM_LEN
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn parse_and_write_naptr() {
94        let naptr = NAPTR {
95            order: 0,
96            preference: 1,
97            flags: CharacterString::new(b"123abc").unwrap(),
98            services: CharacterString::new(b"test").unwrap(),
99            regexp: CharacterString::new(b"@\\w+\\.\\w{2,3}(\\.\\w{2,3})?").unwrap(),
100            replacement: Name::new("e.exchange.com").unwrap(),
101        };
102
103        let mut data = Vec::new();
104        assert!(naptr.write_to(&mut data).is_ok());
105
106        let naptr = NAPTR::parse(&mut data[..].into());
107        assert!(naptr.is_ok());
108        let naptr = naptr.unwrap();
109
110        assert_eq!(data.len(), naptr.len());
111        assert_eq!(0, naptr.order);
112        assert_eq!(1, naptr.preference);
113        assert_eq!("123abc", naptr.flags.to_string());
114        assert_eq!("test", naptr.services.to_string());
115        assert_eq!("@\\w+\\.\\w{2,3}(\\.\\w{2,3})?", naptr.regexp.to_string());
116        assert_eq!("e.exchange.com", naptr.replacement.to_string());
117    }
118}