rsip_dns/records/
naptr_record.rs1use rsip::{Domain, Error, Transport};
2use std::collections::VecDeque;
3use std::convert::TryFrom;
4
5#[derive(Debug, Clone)]
7pub struct NaptrRecord {
8 pub entries: Vec<NaptrEntry>,
9 pub domain: Domain,
10}
11
12#[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#[derive(Debug, Clone)]
26pub enum NaptrFlags {
27 S,
28 A,
29 U,
30 P,
31 Other(Vec<u8>),
32}
33
34#[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}