trust_dns_proto/rr/rdata/naptr.rs
1// Copyright 2015-2019 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! Dynamic Delegation Discovery System
9
10use std::fmt;
11
12#[cfg(feature = "serde-config")]
13use serde::{Deserialize, Serialize};
14
15use crate::error::*;
16use crate::rr::domain::Name;
17use crate::serialize::binary::*;
18
19/// [RFC 3403 DDDS DNS Database, October 2002](https://tools.ietf.org/html/rfc3403#section-4)
20///
21/// ```text
22/// 4.1 Packet Format
23///
24/// The packet format of the NAPTR RR is given below. The DNS type code
25/// for NAPTR is 35.
26///
27/// The packet format for the NAPTR record is as follows
28/// 1 1 1 1 1 1
29/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
30/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
31/// | ORDER |
32/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
33/// | PREFERENCE |
34/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
35/// / FLAGS /
36/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
37/// / SERVICES /
38/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
39/// / REGEXP /
40/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
41/// / REPLACEMENT /
42/// / /
43/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
44///
45/// <character-string> and <domain-name> as used here are defined in RFC
46/// 1035 [7].
47/// ```
48#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
49#[derive(Debug, PartialEq, Eq, Hash, Clone)]
50pub struct NAPTR {
51 order: u16,
52 preference: u16,
53 flags: Box<[u8]>,
54 services: Box<[u8]>,
55 regexp: Box<[u8]>,
56 replacement: Name,
57}
58
59impl NAPTR {
60 /// Constructs a new NAPTR record
61 ///
62 /// # Arguments
63 ///
64 /// * `order` - the order in which the NAPTR records MUST be processed in order to accurately represent the ordered list of Rules.
65 /// * `preference` - this field is equivalent to the Priority value in the DDDS Algorithm.
66 /// * `flags` - flags to control aspects of the rewriting and interpretation of the fields in the record. Flags are single characters from the set A-Z and 0-9.
67 /// * `services` - the Service Parameters applicable to this this delegation path.
68 /// * `regexp` - substitution expression that is applied to the original string held by the client in order to construct the next domain name to lookup.
69 /// * `replacement` - the next domain-name to query for depending on the potential values found in the flags field.
70 pub fn new(
71 order: u16,
72 preference: u16,
73 flags: Box<[u8]>,
74 services: Box<[u8]>,
75 regexp: Box<[u8]>,
76 replacement: Name,
77 ) -> Self {
78 Self {
79 order,
80 preference,
81 flags,
82 services,
83 regexp,
84 replacement,
85 }
86 }
87
88 /// ```text
89 /// ORDER
90 /// A 16-bit unsigned integer specifying the order in which the NAPTR
91 /// records MUST be processed in order to accurately represent the
92 /// ordered list of Rules. The ordering is from lowest to highest.
93 /// If two records have the same order value then they are considered
94 /// to be the same rule and should be selected based on the
95 /// combination of the Preference values and Services offered.
96 /// ```
97 pub fn order(&self) -> u16 {
98 self.order
99 }
100
101 /// ```text
102 /// PREFERENCE
103 /// Although it is called "preference" in deference to DNS
104 /// terminology, this field is equivalent to the Priority value in the
105 /// DDDS Algorithm. It is a 16-bit unsigned integer that specifies
106 /// the order in which NAPTR records with equal Order values SHOULD be
107 /// processed, low numbers being processed before high numbers. This
108 /// is similar to the preference field in an MX record, and is used so
109 /// domain administrators can direct clients towards more capable
110 /// hosts or lighter weight protocols. A client MAY look at records
111 /// with higher preference values if it has a good reason to do so
112 /// such as not supporting some protocol or service very well.
113 ///
114 /// The important difference between Order and Preference is that once
115 /// a match is found the client MUST NOT consider records with a
116 /// different Order but they MAY process records with the same Order
117 /// but different Preferences. The only exception to this is noted in
118 /// the second important Note in the DDDS algorithm specification
119 /// concerning allowing clients to use more complex Service
120 /// determination between steps 3 and 4 in the algorithm. Preference
121 /// is used to give communicate a higher quality of service to rules
122 /// that are considered the same from an authority standpoint but not
123 /// from a simple load balancing standpoint.
124 ///
125 /// It is important to note that DNS contains several load balancing
126 /// mechanisms and if load balancing among otherwise equal services
127 /// should be needed then methods such as SRV records or multiple A
128 /// records should be utilized to accomplish load balancing.
129 /// ```
130 pub fn preference(&self) -> u16 {
131 self.preference
132 }
133
134 /// ```text
135 /// FLAGS
136 /// A <character-string> containing flags to control aspects of the
137 /// rewriting and interpretation of the fields in the record. Flags
138 /// are single characters from the set A-Z and 0-9. The case of the
139 /// alphabetic characters is not significant. The field can be empty.
140 ///
141 /// It is up to the Application specifying how it is using this
142 /// Database to define the Flags in this field. It must define which
143 /// ones are terminal and which ones are not.
144 /// ```
145 pub fn flags(&self) -> &[u8] {
146 &self.flags
147 }
148
149 /// ```text
150 /// SERVICES
151 /// A <character-string> that specifies the Service Parameters
152 /// applicable to this this delegation path. It is up to the
153 /// Application Specification to specify the values found in this
154 /// field.
155 /// ```
156 pub fn services(&self) -> &[u8] {
157 &self.services
158 }
159
160 /// ```text
161 /// REGEXP
162 /// A <character-string> containing a substitution expression that is
163 /// applied to the original string held by the client in order to
164 /// construct the next domain name to lookup. See the DDDS Algorithm
165 /// specification for the syntax of this field.
166 ///
167 /// As stated in the DDDS algorithm, The regular expressions MUST NOT
168 /// be used in a cumulative fashion, that is, they should only be
169 /// applied to the original string held by the client, never to the
170 /// domain name p roduced by a previous NAPTR rewrite. The latter is
171 /// tempting in some applications but experience has shown such use to
172 /// be extremely fault sensitive, very error prone, and extremely
173 /// difficult to debug.
174 /// ```
175 pub fn regexp(&self) -> &[u8] {
176 &self.regexp
177 }
178
179 /// ```text
180 /// REPLACEMENT
181 /// A <domain-name> which is the next domain-name to query for
182 /// depending on the potential values found in the flags field. This
183 /// field is used when the regular expression is a simple replacement
184 /// operation. Any value in this field MUST be a fully qualified
185 /// domain-name. Name compression is not to be used for this field.
186 ///
187 /// This field and the REGEXP field together make up the Substitution
188 /// Expression in the DDDS Algorithm. It is simply a historical
189 /// optimization specifically for DNS compression that this field
190 /// exists. The fields are also mutually exclusive. If a record is
191 /// returned that has values for both fields then it is considered to
192 /// be in error and SHOULD be either ignored or an error returned.
193 /// ```
194 pub fn replacement(&self) -> &Name {
195 &self.replacement
196 }
197}
198
199/// verifies that the flags are valid
200pub fn verify_flags(flags: &[u8]) -> bool {
201 flags
202 .iter()
203 .all(|c| matches!(c, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z'))
204}
205
206/// Read the RData from the given Decoder
207pub fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<NAPTR> {
208 Ok(NAPTR::new(
209 decoder.read_u16()?.unverified(/*any u16 is valid*/),
210 decoder.read_u16()?.unverified(/*any u16 is valid*/),
211 // must be 0-9a-z
212 decoder
213 .read_character_data()?
214 .verify_unwrap(|s| verify_flags(s))
215 .map_err(|_e| ProtoError::from("flags are not within range [a-zA-Z0-9]"))?
216 .to_vec()
217 .into_boxed_slice(),
218 decoder.read_character_data()?.unverified(/*any chardata*/).to_vec().into_boxed_slice(),
219 decoder.read_character_data()?.unverified(/*any chardata*/).to_vec().into_boxed_slice(),
220 Name::read(decoder)?,
221 ))
222}
223
224/// Declares the method for emitting this type
225pub fn emit(encoder: &mut BinEncoder<'_>, naptr: &NAPTR) -> ProtoResult<()> {
226 naptr.order.emit(encoder)?;
227 naptr.preference.emit(encoder)?;
228 encoder.emit_character_data(&naptr.flags)?;
229 encoder.emit_character_data(&naptr.services)?;
230 encoder.emit_character_data(&naptr.regexp)?;
231
232 encoder.with_canonical_names(|encoder| naptr.replacement.emit(encoder))?;
233 Ok(())
234}
235
236/// [RFC 2915](https://tools.ietf.org/html/rfc2915), NAPTR DNS RR, September 2000
237///
238/// ```text
239/// Master File Format
240///
241/// The master file format follows the standard rules in RFC-1035 [1].
242/// Order and preference, being 16-bit unsigned integers, shall be an
243/// integer between 0 and 65535. The Flags and Services and Regexp
244/// fields are all quoted <character-string>s. Since the Regexp field
245/// can contain numerous backslashes and thus should be treated with
246/// care. See Section 10 for how to correctly enter and escape the
247/// regular expression.
248///
249/// ;; order pref flags service regexp replacement
250/// IN NAPTR 100 50 "a" "z3950+N2L+N2C" "" cidserver.example.com.
251/// IN NAPTR 100 50 "a" "rcds+N2C" "" cidserver.example.com.
252/// IN NAPTR 100 50 "s" "http+N2L+N2C+N2R" "" www.example.com.
253/// ```
254impl fmt::Display for NAPTR {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
256 write!(
257 f,
258 "{order} {pref} \"{flags}\" \"{service}\" \"{regexp}\" {replace}",
259 order = self.order,
260 pref = self.preference,
261 flags = &String::from_utf8_lossy(&self.flags),
262 service = &String::from_utf8_lossy(&self.services),
263 regexp = &String::from_utf8_lossy(&self.regexp),
264 replace = self.replacement
265 )
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 #![allow(clippy::dbg_macro, clippy::print_stdout)]
272
273 use super::*;
274 #[test]
275 fn test() {
276 use std::str::FromStr;
277
278 let rdata = NAPTR::new(
279 8,
280 16,
281 b"aa11AA".to_vec().into_boxed_slice(),
282 b"services".to_vec().into_boxed_slice(),
283 b"regexpr".to_vec().into_boxed_slice(),
284 Name::from_str("naptr.example.com").unwrap(),
285 );
286
287 let mut bytes = Vec::new();
288 let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
289 assert!(emit(&mut encoder, &rdata).is_ok());
290 let bytes = encoder.into_bytes();
291
292 println!("bytes: {:?}", bytes);
293
294 let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
295 let read_rdata = read(&mut decoder).expect("Decoding error");
296 assert_eq!(rdata, read_rdata);
297 }
298
299 #[test]
300 fn test_bad_data() {
301 use std::str::FromStr;
302
303 let rdata = NAPTR::new(
304 8,
305 16,
306 b"aa11AA-".to_vec().into_boxed_slice(),
307 b"services".to_vec().into_boxed_slice(),
308 b"regexpr".to_vec().into_boxed_slice(),
309 Name::from_str("naptr.example.com").unwrap(),
310 );
311
312 let mut bytes = Vec::new();
313 let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
314 assert!(emit(&mut encoder, &rdata).is_ok());
315 let bytes = encoder.into_bytes();
316
317 println!("bytes: {:?}", bytes);
318
319 let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
320 let read_rdata = read(&mut decoder);
321 assert!(
322 read_rdata.is_err(),
323 "should have failed decoding with bad flag data"
324 );
325 }
326}