domain/base/name/traits.rs
1//! Domain name-related traits.
2//!
3//! This is a private module. Its public traits are re-exported by the parent.
4
5use super::chain::{Chain, LongChainError};
6use super::dname::Dname;
7use super::label::Label;
8use super::relative::RelativeDname;
9#[cfg(feature = "bytes")]
10use bytes::Bytes;
11use core::cmp;
12use core::convert::Infallible;
13use octseq::builder::{
14 infallible, BuilderAppendError, EmptyBuilder, FreezeBuilder, FromBuilder,
15 OctetsBuilder, ShortBuf,
16};
17#[cfg(feature = "std")]
18use std::borrow::Cow;
19
20//------------ ToLabelIter ---------------------------------------------------
21
22/// A type that can produce an iterator over its labels.
23///
24/// This trait is used as a trait bound for both [`ToDname`] and
25/// [`ToRelativeDname`]. It is separate since it has to be generic over the
26/// lifetime of the label reference but we don’t want to have this lifetime
27/// parameter pollute those traits.
28///
29/// [`ToDname`]: trait.ToDname.html
30/// [`ToRelativeDname`]: trait ToRelativeDname.html
31#[allow(clippy::len_without_is_empty)]
32pub trait ToLabelIter {
33 /// The type of the iterator over the labels.
34 ///
35 /// This iterator types needs to be double ended so that we can deal with
36 /// name suffixes. It needs to be cloneable to be able to cascade over
37 /// parents of a name.
38 type LabelIter<'a>: Iterator<Item = &'a Label>
39 + DoubleEndedIterator
40 + Clone
41 where
42 Self: 'a;
43
44 /// Returns an iterator over the labels.
45 fn iter_labels(&self) -> Self::LabelIter<'_>;
46
47 /// Returns the length in octets of the encoded name.
48 fn compose_len(&self) -> u16 {
49 self.iter_labels().map(|label| label.compose_len()).sum()
50 }
51
52 /// Determines whether `base` is a prefix of `self`.
53 fn starts_with<N: ToLabelIter + ?Sized>(&self, base: &N) -> bool {
54 let mut self_iter = self.iter_labels();
55 let mut base_iter = base.iter_labels();
56 loop {
57 match (self_iter.next(), base_iter.next()) {
58 (Some(sl), Some(bl)) => {
59 if sl != bl {
60 return false;
61 }
62 }
63 (_, None) => return true,
64 (None, Some(_)) => return false,
65 }
66 }
67 }
68
69 /// Determines whether `base` is a suffix of `self`.
70 fn ends_with<N: ToLabelIter + ?Sized>(&self, base: &N) -> bool {
71 let mut self_iter = self.iter_labels();
72 let mut base_iter = base.iter_labels();
73 loop {
74 match (self_iter.next_back(), base_iter.next_back()) {
75 (Some(sl), Some(bl)) => {
76 if sl != bl {
77 return false;
78 }
79 }
80 (_, None) => return true,
81 (None, Some(_)) => return false,
82 }
83 }
84 }
85}
86
87impl<'r, N: ToLabelIter + ?Sized> ToLabelIter for &'r N {
88 type LabelIter<'a> = N::LabelIter<'a> where 'r: 'a, N: 'a;
89
90 fn iter_labels(&self) -> Self::LabelIter<'_> {
91 (*self).iter_labels()
92 }
93}
94
95//------------ ToDname -------------------------------------------------------
96
97/// A type that represents an absolute domain name.
98///
99/// An absolute domain name is a sequence of labels where the last label is
100/// the root label and where the wire-format representation is not longer than
101/// 255 characters. Implementers of this trait need to provide access to the
102/// label sequence via an iterator and know how to compose the wire-format
103/// representation into a buffer.
104///
105/// The most common types implementing this trait are [`Dname`],
106/// [`ParsedDname`], and [`Chain<L, R>`] where `R` is `ToDname` itself.
107///
108/// [`Chain<L, R>`]: struct.Chain.html
109/// [`Dname`]: struct.Dname.html
110/// [`ParsedDname`]: struct.ParsedDname.html
111pub trait ToDname: ToLabelIter {
112 /// Converts the name into a single, uncompressed name.
113 ///
114 /// The default implementation provided by the trait iterates over the
115 /// labels of the name and adds them one by one to [`Dname`]. This will
116 /// work for any name but an optimized implementation can be provided for
117 /// some types of names.
118 ///
119 /// [`Dname`]: struct.Dname.html
120 fn to_dname<Octets>(
121 &self,
122 ) -> Result<Dname<Octets>, BuilderAppendError<Octets>>
123 where
124 Octets: FromBuilder,
125 <Octets as FromBuilder>::Builder: EmptyBuilder,
126 {
127 let mut builder =
128 Octets::Builder::with_capacity(self.compose_len().into());
129 self.iter_labels()
130 .try_for_each(|label| label.compose(&mut builder))?;
131 Ok(unsafe { Dname::from_octets_unchecked(builder.freeze()) })
132 }
133
134 /// Converts the name into a single name in canonical form.
135 fn to_canonical_dname<Octets>(
136 &self,
137 ) -> Result<Dname<Octets>, BuilderAppendError<Octets>>
138 where
139 Octets: FromBuilder,
140 <Octets as FromBuilder>::Builder: EmptyBuilder,
141 {
142 let mut builder =
143 Octets::Builder::with_capacity(self.compose_len().into());
144 self.iter_labels()
145 .try_for_each(|label| label.compose_canonical(&mut builder))?;
146 Ok(unsafe { Dname::from_octets_unchecked(builder.freeze()) })
147 }
148
149 /// Returns an octets slice of the content if possible.
150 ///
151 /// If a value stores the domain name as one single octets sequence, it
152 /// should return a reference to this sequence here. If the name is
153 /// composed from multiple such sequences, it should return `None`.
154 ///
155 /// This method is used to optimize comparision operations between
156 /// two values that are indeed flat names.
157 fn as_flat_slice(&self) -> Option<&[u8]> {
158 None
159 }
160
161 fn compose<Target: OctetsBuilder + ?Sized>(
162 &self,
163 target: &mut Target,
164 ) -> Result<(), Target::AppendError> {
165 if let Some(slice) = self.as_flat_slice() {
166 target.append_slice(slice)
167 } else {
168 for label in self.iter_labels() {
169 label.compose(target)?;
170 }
171 Ok(())
172 }
173 }
174
175 fn compose_canonical<Target: OctetsBuilder + ?Sized>(
176 &self,
177 target: &mut Target,
178 ) -> Result<(), Target::AppendError> {
179 for label in self.iter_labels() {
180 label.compose_canonical(target)?;
181 }
182 Ok(())
183 }
184
185 /// Returns a cow of the domain name.
186 ///
187 /// If the name is available as one single slice – i.e.,
188 /// [`as_flat_slice`] returns ‘some,’ creates the borrowed variant from
189 /// that slice. Otherwise assembles an owned variant via [`to_dname`].
190 ///
191 /// [`as_flat_slice`]: #method.as_flat_slice
192 /// [`to_dname`]: #method.to_dname
193 #[cfg(feature = "std")]
194 fn to_cow(&self) -> Dname<std::borrow::Cow<[u8]>> {
195 let octets = self
196 .as_flat_slice()
197 .map(Cow::Borrowed)
198 .unwrap_or_else(|| Cow::Owned(self.to_vec().into_octets()));
199 unsafe { Dname::from_octets_unchecked(octets) }
200 }
201
202 /// Returns the domain name assembled into a `Vec<u8>`.
203 #[cfg(feature = "std")]
204 fn to_vec(&self) -> Dname<std::vec::Vec<u8>> {
205 infallible(self.to_dname())
206 }
207
208 /// Returns the domain name assembled into a bytes value.
209 #[cfg(feature = "bytes")]
210 fn to_bytes(&self) -> Dname<Bytes> {
211 infallible(self.to_dname())
212 }
213
214 /// Tests whether `self` and `other` are equal.
215 ///
216 /// This method can be used to implement `PartialEq` on types implementing
217 /// `ToDname` since a blanket implementation for all pairs of `ToDname`
218 /// is currently impossible.
219 ///
220 /// Domain names are compared ignoring ASCII case.
221 fn name_eq<N: ToDname + ?Sized>(&self, other: &N) -> bool {
222 if let (Some(left), Some(right)) =
223 (self.as_flat_slice(), other.as_flat_slice())
224 {
225 // We can do this because the length octets of each label are in
226 // the ranged 0..64 which is before all ASCII letters.
227 left.eq_ignore_ascii_case(right)
228 } else {
229 self.iter_labels().eq(other.iter_labels())
230 }
231 }
232
233 /// Returns the ordering between `self` and `other`.
234 ///
235 /// This method can be used to implement both `PartialOrd` and `Ord` on
236 /// types implementing `ToDname` since a blanket implementation for all
237 /// pairs of `ToDname`s is currently not possible.
238 ///
239 /// Domain name order is determined according to the ‘canonical DNS
240 /// name order’ as defined in [section 6.1 of RFC 4034][RFC4034-6.1].
241 ///
242 /// [RFC4034-6.1]: https://tools.ietf.org/html/rfc4034#section-6.1
243 fn name_cmp<N: ToDname + ?Sized>(&self, other: &N) -> cmp::Ordering {
244 let mut self_iter = self.iter_labels();
245 let mut other_iter = other.iter_labels();
246 loop {
247 match (self_iter.next_back(), other_iter.next_back()) {
248 (Some(left), Some(right)) => match left.cmp(right) {
249 cmp::Ordering::Equal => {}
250 res => return res,
251 },
252 (None, Some(_)) => return cmp::Ordering::Less,
253 (Some(_), None) => return cmp::Ordering::Greater,
254 (None, None) => return cmp::Ordering::Equal,
255 }
256 }
257 }
258
259 /// Returns the composed name ordering.
260 fn composed_cmp<N: ToDname + ?Sized>(&self, other: &N) -> cmp::Ordering {
261 if let (Some(left), Some(right)) =
262 (self.as_flat_slice(), other.as_flat_slice())
263 {
264 return left.cmp(right);
265 }
266 let mut self_iter = self.iter_labels();
267 let mut other_iter = other.iter_labels();
268 loop {
269 match (self_iter.next(), other_iter.next()) {
270 (Some(left), Some(right)) => match left.composed_cmp(right) {
271 cmp::Ordering::Equal => {}
272 other => return other,
273 },
274 (None, None) => return cmp::Ordering::Equal,
275 _ => {
276 // The root label sorts before any other label, so we
277 // can never end up in a situation where one name runs
278 // out of labels while comparing equal.
279 unreachable!()
280 }
281 }
282 }
283 }
284
285 /// Returns the lowercase composed ordering.
286 fn lowercase_composed_cmp<N: ToDname + ?Sized>(
287 &self,
288 other: &N,
289 ) -> cmp::Ordering {
290 // Since there isn’t a `cmp_ignore_ascii_case` on slice, we don’t
291 // gain much from the shortcut.
292 let mut self_iter = self.iter_labels();
293 let mut other_iter = other.iter_labels();
294 loop {
295 match (self_iter.next(), other_iter.next()) {
296 (Some(left), Some(right)) => {
297 match left.lowercase_composed_cmp(right) {
298 cmp::Ordering::Equal => {}
299 other => return other,
300 }
301 }
302 (None, None) => return cmp::Ordering::Equal,
303 _ => {
304 // The root label sorts before any other label, so we
305 // can never end up in a situation where one name runs
306 // out of labels while comparing equal.
307 unreachable!()
308 }
309 }
310 }
311 }
312
313 /// Returns the number of labels for the RRSIG Labels field.
314 ///
315 /// This is the actual number of labels without counting the root label
316 /// or a possible initial asterisk label.
317 fn rrsig_label_count(&self) -> u8 {
318 let mut labels = self.iter_labels();
319 if labels.next().unwrap().is_wildcard() {
320 (labels.count() - 1) as u8
321 } else {
322 labels.count() as u8
323 }
324 }
325}
326
327impl<'a, N: ToDname + ?Sized + 'a> ToDname for &'a N {}
328
329//------------ ToRelativeDname -----------------------------------------------
330
331/// A type that represents a relative domain name.
332///
333/// In order to be a relative domain name, a type needs to be able to
334/// provide a sequence of labels via an iterator where the last label is not
335/// the root label. The type also needs to be able to compose the wire-format
336/// representation of the domain name it represents which must not be longer
337/// than 254 characters. This limit has been chosen so that by attaching the
338/// one character long root label, a valid absolute name can be constructed
339/// from the relative name.
340///
341/// The most important types implementing this trait are [`RelativeDname`]
342/// and [`Chain<L,R>`] where `R` is a `ToRelativeDname` itself.
343///
344/// [`Chain<L, R>`]: struct.Chain.html
345/// [`RelativeDname`]: struct.RelativeDname.html
346pub trait ToRelativeDname: ToLabelIter {
347 /// Converts the name into a single, continous name.
348 ///
349 /// The canonical implementation provided by the trait iterates over the
350 /// labels of the name and adds them one by one to [`RelativeDname`].
351 /// This will work for any name but an optimized implementation can be
352 /// provided for
353 /// some types of names.
354 ///
355 /// [`RelativeDname`]: struct.RelativeDname.html
356 fn to_relative_dname<Octets>(
357 &self,
358 ) -> Result<RelativeDname<Octets>, BuilderAppendError<Octets>>
359 where
360 Octets: FromBuilder,
361 <Octets as FromBuilder>::Builder: EmptyBuilder,
362 {
363 let mut builder =
364 Octets::Builder::with_capacity(self.compose_len().into());
365 self.iter_labels()
366 .try_for_each(|label| label.compose(&mut builder))?;
367 Ok(unsafe { RelativeDname::from_octets_unchecked(builder.freeze()) })
368 }
369
370 /// Converts the name into a single name in canonical form.
371 fn to_canonical_relative_dname<Octets>(
372 &self,
373 ) -> Result<RelativeDname<Octets>, BuilderAppendError<Octets>>
374 where
375 Octets: FromBuilder,
376 <Octets as FromBuilder>::Builder: EmptyBuilder,
377 {
378 let mut builder =
379 Octets::Builder::with_capacity(self.compose_len().into());
380 self.iter_labels()
381 .try_for_each(|label| label.compose_canonical(&mut builder))?;
382 Ok(unsafe { RelativeDname::from_octets_unchecked(builder.freeze()) })
383 }
384
385 /// Returns a byte slice of the content if possible.
386 ///
387 /// This method can is used to optimize comparision operations between
388 /// two values that are indeed flat names.
389 fn as_flat_slice(&self) -> Option<&[u8]> {
390 None
391 }
392
393 fn compose<Target: OctetsBuilder + ?Sized>(
394 &self,
395 target: &mut Target,
396 ) -> Result<(), Target::AppendError> {
397 if let Some(slice) = self.as_flat_slice() {
398 target.append_slice(slice)
399 } else {
400 for label in self.iter_labels() {
401 label.compose(target)?;
402 }
403 Ok(())
404 }
405 }
406
407 fn compose_canonical<Target: OctetsBuilder + ?Sized>(
408 &self,
409 target: &mut Target,
410 ) -> Result<(), Target::AppendError> {
411 for label in self.iter_labels() {
412 label.compose_canonical(target)?;
413 }
414 Ok(())
415 }
416
417 /// Returns a cow of the relative domain name.
418 ///
419 /// If the name is available as one single slice – i.e.,
420 /// [`as_flat_slice`] returns ‘some,’ creates the borrowed variant from
421 /// that slice. Otherwise assembles an owned variant via [`to_dname`].
422 ///
423 /// [`as_flat_slice`]: #method.as_flat_slice
424 /// [`to_dname`]: #method.to_dname
425 #[cfg(feature = "std")]
426 fn to_cow(&self) -> RelativeDname<std::borrow::Cow<[u8]>> {
427 let octets = self
428 .as_flat_slice()
429 .map(Cow::Borrowed)
430 .unwrap_or_else(|| Cow::Owned(self.to_vec().into_octets()));
431 unsafe { RelativeDname::from_octets_unchecked(octets) }
432 }
433
434 /// Returns the domain name assembled into a `Vec<u8>`.
435 #[cfg(feature = "std")]
436 fn to_vec(&self) -> RelativeDname<std::vec::Vec<u8>> {
437 infallible(self.to_relative_dname())
438 }
439
440 /// Returns the domain name assembled into a bytes value.
441 #[cfg(feature = "bytes")]
442 fn to_bytes(&self) -> RelativeDname<Bytes> {
443 infallible(self.to_relative_dname())
444 }
445
446 /// Returns whether the name is empty.
447 fn is_empty(&self) -> bool {
448 self.iter_labels().next().is_none()
449 }
450
451 /// Returns a chain of this name and the provided name.
452 fn chain<N: ToLabelIter>(
453 self,
454 suffix: N,
455 ) -> Result<Chain<Self, N>, LongChainError>
456 where
457 Self: Sized,
458 {
459 Chain::new(self, suffix)
460 }
461
462 /// Returns the absolute name by chaining it with the root label.
463 fn chain_root(self) -> Chain<Self, Dname<&'static [u8]>>
464 where
465 Self: Sized,
466 {
467 // Appending the root label will always work.
468 Chain::new(self, Dname::root()).unwrap()
469 }
470
471 /// Tests whether `self` and `other` are equal.
472 ///
473 /// This method can be used to implement `PartialEq` on types implementing
474 /// `ToDname` since a blanket implementation for all pairs of `ToDname`
475 /// is currently impossible.
476 ///
477 /// Domain names are compared ignoring ASCII case.
478 fn name_eq<N: ToRelativeDname + ?Sized>(&self, other: &N) -> bool {
479 if let (Some(left), Some(right)) =
480 (self.as_flat_slice(), other.as_flat_slice())
481 {
482 left.eq_ignore_ascii_case(right)
483 } else {
484 self.iter_labels().eq(other.iter_labels())
485 }
486 }
487
488 /// Returns the ordering between `self` and `other`.
489 ///
490 /// This method can be used to implement both `PartialOrd` and `Ord` on
491 /// types implementing `ToDname` since a blanket implementation for all
492 /// pairs of `ToDname`s is currently not possible.
493 ///
494 /// Domain name order is determined according to the ‘canonical DNS
495 /// name order’ as defined in [section 6.1 of RFC 4034][RFC4034-6.1].
496 /// This section describes how absolute domain names are ordered only.
497 /// We will order relative domain names according to these rules as if
498 /// they had the same origin, i.e., as if they were relative to the
499 /// same name.
500 ///
501 /// [RFC4034-6.1]: https://tools.ietf.org/html/rfc4034#section-6.1
502 fn name_cmp<N: ToRelativeDname + ?Sized>(
503 &self,
504 other: &N,
505 ) -> cmp::Ordering {
506 let mut self_iter = self.iter_labels();
507 let mut other_iter = other.iter_labels();
508 loop {
509 match (self_iter.next_back(), other_iter.next_back()) {
510 (Some(left), Some(right)) => match left.cmp(right) {
511 cmp::Ordering::Equal => {}
512 res => return res,
513 },
514 (None, Some(_)) => return cmp::Ordering::Less,
515 (Some(_), None) => return cmp::Ordering::Greater,
516 (None, None) => return cmp::Ordering::Equal,
517 }
518 }
519 }
520}
521
522impl<'a, N: ToRelativeDname + ?Sized + 'a> ToRelativeDname for &'a N {}
523
524//------------ FlattenInto ---------------------------------------------------
525
526pub trait FlattenInto<Target>: Sized {
527 type AppendError: Into<ShortBuf>;
528
529 fn try_flatten_into(self) -> Result<Target, Self::AppendError>;
530
531 fn flatten_into(self) -> Target
532 where
533 Self::AppendError: Into<Infallible>,
534 {
535 infallible(self.try_flatten_into())
536 }
537}