simple_dns/dns/rdata/
naptr.rs

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