rsip_dns/records/
naptr_record.rs

1use rsip::{Domain, Error, Transport};
2use std::collections::VecDeque;
3use std::convert::TryFrom;
4
5/// Simple struct that holds the NAPTR record details (domain and srv entries)
6#[derive(Debug, Clone)]
7pub struct NaptrRecord {
8    pub entries: Vec<NaptrEntry>,
9    pub domain: Domain,
10}
11
12/// Simple struct that resembles the NAPTR record entries
13#[derive(Debug, Clone)]
14pub struct NaptrEntry {
15    pub order: u16,
16    pub preference: u16,
17    pub flags: NaptrFlags,
18    pub services: NaptrServices,
19    pub regexp: Vec<u8>,
20    pub replacement: Domain,
21}
22
23//TODO: this should be a vec of NaptrFlag, with some handy methods to check if there is only 1
24//specific flag or a specific flag is contained (in our case S flag is what we care)
25#[derive(Debug, Clone)]
26pub enum NaptrFlags {
27    S,
28    A,
29    U,
30    P,
31    Other(Vec<u8>),
32}
33
34//TODO: this should be a vec of NaptrServices, with some handy methods to check if there is only 1
35//specific service or a specific service is contained (in our case SIP(S)+D2x services is what we care)
36#[derive(Debug, Clone)]
37pub enum NaptrServices {
38    SipD2t,
39    SipD2u,
40    SipD2s,
41    SipD2w,
42    SipsD2t,
43    SipsD2u,
44    SipsD2s,
45    SipsD2w,
46    Other(String),
47}
48
49impl From<&[u8]> for NaptrFlags {
50    fn from(from: &[u8]) -> Self {
51        match from {
52            s if s == b"S" => Self::S,
53            s if s == b"A" => Self::A,
54            s if s == b"A" => Self::U,
55            s if s == b"P" => Self::P,
56            s => Self::Other(s.to_vec()),
57        }
58    }
59}
60
61impl NaptrEntry {
62    pub fn total_weight(&self) -> u16 {
63        self.order + self.preference
64    }
65}
66
67impl From<NaptrRecord> for Vec<NaptrEntry> {
68    fn from(from: NaptrRecord) -> Self {
69        from.entries
70    }
71}
72
73impl From<NaptrRecord> for VecDeque<NaptrEntry> {
74    fn from(from: NaptrRecord) -> Self {
75        from.entries.into()
76    }
77}
78
79impl NaptrRecord {
80    pub fn as_slice(&self) -> &[NaptrEntry] {
81        self.entries.as_slice()
82    }
83
84    pub fn iter(&self) -> impl Iterator<Item = &NaptrEntry> {
85        self.entries.iter()
86    }
87
88    pub fn sorted(mut self) -> Self {
89        use std::cmp::Reverse;
90
91        self.entries.sort_by_key(|b| Reverse(b.total_weight()));
92        self
93    }
94}
95
96impl IntoIterator for NaptrRecord {
97    type Item = NaptrEntry;
98    type IntoIter = std::vec::IntoIter<Self::Item>;
99
100    fn into_iter(self) -> Self::IntoIter {
101        self.entries.into_iter()
102    }
103}
104
105impl NaptrServices {
106    pub fn transport(&self) -> Option<Transport> {
107        match self {
108            Self::SipD2t => Some(Transport::Tcp),
109            Self::SipD2u => Some(Transport::Udp),
110            Self::SipD2s => Some(Transport::Sctp),
111            Self::SipD2w => Some(Transport::Ws),
112            Self::SipsD2t => Some(Transport::Tls),
113            Self::SipsD2u => None,
114            Self::SipsD2s => None,
115            Self::SipsD2w => Some(Transport::Wss),
116            _ => None,
117        }
118    }
119
120    pub fn secure(&self) -> bool {
121        match self {
122            Self::SipD2t => false,
123            Self::SipD2u => false,
124            Self::SipD2s => false,
125            Self::SipD2w => false,
126            Self::SipsD2t => true,
127            Self::SipsD2u => true,
128            Self::SipsD2s => true,
129            Self::SipsD2w => true,
130            _ => false,
131        }
132    }
133}
134
135impl TryFrom<&[u8]> for NaptrServices {
136    type Error = Error;
137
138    fn try_from(from: &[u8]) -> Result<Self, Self::Error> {
139        use std::str::from_utf8;
140
141        match from_utf8(from)? {
142            part if part.eq_ignore_ascii_case("SIP+D2T") => Ok(Self::SipD2t),
143            part if part.eq_ignore_ascii_case("SIP+D2U") => Ok(Self::SipD2u),
144            part if part.eq_ignore_ascii_case("SIP+D2S") => Ok(Self::SipD2s),
145            part if part.eq_ignore_ascii_case("SIP+D2W") => Ok(Self::SipD2w),
146            part if part.eq_ignore_ascii_case("SIPS+D2T") => Ok(Self::SipsD2t),
147            part if part.eq_ignore_ascii_case("SIPS+D2U") => Ok(Self::SipsD2u),
148            part if part.eq_ignore_ascii_case("SIPS+D2S") => Ok(Self::SipsD2s),
149            part if part.eq_ignore_ascii_case("SIPS+D2W") => Ok(Self::SipsD2w),
150            part => Err(Error::ParseError(format!("unknown transport: {}", part))),
151        }
152    }
153}
154
155#[cfg(feature = "test-utils")]
156impl testing_utils::Randomize for NaptrEntry {
157    fn random() -> Self {
158        let services = testing_utils::sample(&[
159            NaptrServices::SipD2t,
160            NaptrServices::SipD2u,
161            NaptrServices::SipD2s,
162            NaptrServices::SipD2w,
163            NaptrServices::SipsD2t,
164            NaptrServices::SipsD2u,
165            NaptrServices::SipsD2s,
166            NaptrServices::SipsD2w,
167        ]);
168
169        Self {
170            order: testing_utils::rand_num_from(0..10),
171            preference: testing_utils::rand_num_from(0..10),
172            flags: NaptrFlags::S,
173            services,
174            regexp: vec![],
175            replacement: "_sip".into(),
176        }
177    }
178}