domain_core/bits/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 std::cmp;
6use bytes::BytesMut;
7use ::bits::compose::{Compose, Compress};
8use super::chain::{Chain, LongChainError};
9use super::dname::Dname;
10use super::label::Label;
11use super::relative::RelativeDname;
12
13
14//------------ ToLabelIter ---------------------------------------------------
15
16/// A type that can produce an iterator over its labels.
17///
18/// This trait is used as a trait bound for both [`ToDname`] and
19/// [`ToRelativeDname`]. It is separate since it has to be generic over the
20/// lifetime of the label reference but we don’t want to have this lifetime
21/// parameter pollute those traits.
22///
23/// [`ToDname`]: trait.ToDname.html
24/// [`ToRelativeDname`]: trait ToRelativeDname.html
25pub trait ToLabelIter<'a> {
26 /// The type of the iterator over the labels.
27 ///
28 /// This iterator types needs to be double ended so that we can deal with
29 /// name suffixes.
30 type LabelIter: Iterator<Item=&'a Label> + DoubleEndedIterator;
31
32 /// Returns an iterator over the labels.
33 fn iter_labels(&'a self) -> Self::LabelIter;
34
35 /// Determines whether `base` is a prefix of `self`.
36 fn starts_with<N: ToLabelIter<'a>>(&'a self, base: &'a N) -> bool {
37 let mut self_iter = self.iter_labels();
38 let mut base_iter = base.iter_labels();
39 loop {
40 match (self_iter.next(), base_iter.next()) {
41 (Some(sl), Some(bl)) => {
42 if sl != bl { return false }
43 }
44 (_, None) => return true,
45 (None, Some(_)) => return false,
46 }
47 }
48 }
49
50 /// Determines whether `base` is a suffix of `self`.
51 fn ends_with<N: ToLabelIter<'a>>(&'a self, base: &'a N) -> bool {
52 let mut self_iter = self.iter_labels();
53 let mut base_iter = base.iter_labels();
54 loop {
55 match (self_iter.next_back(), base_iter.next_back()) {
56 (Some(sl), Some(bl)) => {
57 if sl != bl { return false }
58 }
59 (_, None) => return true,
60 (None, Some(_)) => return false
61 }
62 }
63 }
64}
65
66impl<'a, 'b, N: ToLabelIter<'b>> ToLabelIter<'b> for &'a N {
67 type LabelIter = N::LabelIter;
68
69 fn iter_labels(&'b self) -> Self::LabelIter {
70 (*self).iter_labels()
71 }
72}
73
74
75//------------ ToDname -------------------------------------------------------
76
77/// A type that represents an absolute domain name.
78///
79/// An absolute domain name is a sequence of labels where the last label is
80/// the root label and where the wire-format representation is not longer than
81/// 255 characters. Implementers of this trait need to provide access to the
82/// label sequence via an iterator and know how to compose the wire-format
83/// representation into a buffer.
84///
85/// The most common types implementing this trait are [`Dname`],
86/// [`ParsedDname`], and [`Chain<L, R>`] where `R` is `ToDname` itself.
87///
88/// [`Chain<L, R>`]: struct.Chain.html
89/// [`Dname`]: struct.Dname.html
90/// [`ParsedDname`]: struct.ParsedDname.html
91pub trait ToDname: Compose + Compress + for<'a> ToLabelIter<'a> {
92 /// Creates an uncompressed value of the domain name.
93 ///
94 /// The method has a default implementation that composes the name into
95 /// a new buffer and returns this buffer. If the implementing type can
96 /// create a `Dname` more efficiently, then it should provide its
97 /// own implementation.
98 fn to_name(&self) -> Dname {
99 let mut bytes = BytesMut::with_capacity(self.compose_len());
100 self.compose(&mut bytes);
101 unsafe {
102 Dname::from_bytes_unchecked(bytes.freeze())
103 }
104 }
105
106 /// Returns a byte slice of the content if possible.
107 ///
108 /// If a value stores the domain name as one single byte sequence, it
109 /// should return a reference to this sequence here. If the name is
110 /// composed from multiple such sequences, it should return `None`.
111 ///
112 /// This method is used to optimize comparision operations between
113 /// two values that are indeed flat names.
114 fn as_flat_slice(&self) -> Option<&[u8]> {
115 None
116 }
117
118 /// Tests whether `self` and `other` are equal.
119 ///
120 /// This method can be used to implement `PartialEq` on types implementing
121 /// `ToDname` since a blanket implementation for all pairs of `ToDname`
122 /// is currently impossible.
123 ///
124 /// Domain names are compared ignoring ASCII case.
125 fn name_eq<N: ToDname>(&self, other: &N) -> bool {
126 if let (Some(left), Some(right)) = (self.as_flat_slice(),
127 other.as_flat_slice()) {
128 left.eq_ignore_ascii_case(right)
129 }
130 else {
131 self.iter_labels().eq(other.iter_labels())
132 }
133 }
134
135 /// Returns the ordering between `self` and `other`.
136 ///
137 /// This method can be used to implement both `PartialOrd` and `Ord` on
138 /// types implementing `ToDname` since a blanket implementation for all
139 /// pairs of `ToDname`s is currently not possible.
140 ///
141 /// Domain name order is determined according to the ‘canonical DNS
142 /// name order’ as defined in [section 6.1 of RFC 4034][RFC4034-6.1].
143 ///
144 /// [RFC4034-6.1]: https://tools.ietf.org/html/rfc4034#section-6.1
145 fn name_cmp<N: ToDname>(&self, other: &N) -> cmp::Ordering {
146 let mut self_iter = self.iter_labels();
147 let mut other_iter = other.iter_labels();
148 loop {
149 match (self_iter.next_back(), other_iter.next_back()) {
150 (Some(left), Some(right)) => {
151 match left.cmp(right) {
152 cmp::Ordering::Equal => {}
153 res => return res
154 }
155 }
156 (None, Some(_)) => return cmp::Ordering::Less,
157 (Some(_), None) => return cmp::Ordering::Greater,
158 (None, None) => return cmp::Ordering::Equal
159 }
160 }
161 }
162}
163
164impl<'a, N: ToDname + 'a> ToDname for &'a N { }
165
166
167//------------ ToRelativeDname -----------------------------------------------
168
169/// A type that represents a relative domain name.
170///
171/// In order to be a relative domain name, a type needs to be able to
172/// provide a sequence of labels via an iterator where the last label is not
173/// the root label. The type also needs to be able to compose the wire-format
174/// representation of the domain name it represents which must not be longer
175/// than 254 characters. This limit has been chosen so that by attaching the
176/// one character long root label, a valid absolute name can be constructed
177/// from the relative name.
178///
179/// The most important types implementing this trait are [`RelativeDname`]
180/// and [`Chain<L,R>`] where `R` is a `ToRelativeDname` itself.
181///
182/// [`Chain<L, R>`]: struct.Chain.html
183/// [`RelativeDname`]: struct.RelativeDname.html
184pub trait ToRelativeDname: Compose + for<'a> ToLabelIter<'a> {
185 /// Creates an uncompressed value of the domain name.
186 ///
187 /// The method has a default implementation that composes the name into
188 /// a new buffer and returns this buffer. If the implementing type can
189 /// create a `RelativeDname` more efficiently, then it should provide its
190 /// own implementation.
191 fn to_name(&self) -> RelativeDname {
192 let mut bytes = BytesMut::with_capacity(self.compose_len());
193 self.compose(&mut bytes);
194 unsafe {
195 RelativeDname::from_bytes_unchecked(bytes.freeze())
196 }
197 }
198
199 /// Returns a byte slice of the content if possible.
200 ///
201 /// This method can is used to optimize comparision operations between
202 /// two values that are indeed flat names.
203 fn as_flat_slice(&self) -> Option<&[u8]> {
204 None
205 }
206
207 /// Returns a chain of this name and the provided absolute name.
208 fn chain<N: Compose>(
209 self,
210 suffix: N
211 ) -> Result<Chain<Self, N>, LongChainError>
212 where Self: Sized {
213 Chain::new(self, suffix)
214 }
215
216 /// Returns the absolute name by chaining it with the root label.
217 fn chain_root(self) -> Chain<Self, Dname>
218 where Self: Sized {
219 // Appending the root label will always work.
220 Chain::new(self, Dname::root()).unwrap()
221 }
222
223 /// Tests whether `self` and `other` are equal.
224 ///
225 /// This method can be used to implement `PartialEq` on types implementing
226 /// `ToDname` since a blanket implementation for all pairs of `ToDname`
227 /// is currently impossible.
228 ///
229 /// Domain names are compared ignoring ASCII case.
230 fn name_eq<N: ToRelativeDname>(&self, other: &N) -> bool {
231 if let (Some(left), Some(right)) = (self.as_flat_slice(),
232 other.as_flat_slice()) {
233 left.eq_ignore_ascii_case(right)
234 }
235 else {
236 self.iter_labels().eq(other.iter_labels())
237 }
238 }
239
240 /// Returns the ordering between `self` and `other`.
241 ///
242 /// This method can be used to implement both `PartialOrd` and `Ord` on
243 /// types implementing `ToDname` since a blanket implementation for all
244 /// pairs of `ToDname`s is currently not possible.
245 ///
246 /// Domain name order is determined according to the ‘canonical DNS
247 /// name order’ as defined in [section 6.1 of RFC 4034][RFC4034-6.1].
248 /// This section describes how absolute domain names are ordered only.
249 /// We will order relative domain names according to these rules as if
250 /// they had the same origin, i.e., as if they were relative to the
251 /// same name.
252 ///
253 /// [RFC4034-6.1]: https://tools.ietf.org/html/rfc4034#section-6.1
254 fn name_cmp<N: ToRelativeDname>(&self, other: &N) -> cmp::Ordering {
255 let mut self_iter = self.iter_labels();
256 let mut other_iter = other.iter_labels();
257 loop {
258 match (self_iter.next_back(), other_iter.next_back()) {
259 (Some(left), Some(right)) => {
260 match left.cmp(right) {
261 cmp::Ordering::Equal => {}
262 res => return res
263 }
264 }
265 (None, Some(_)) => return cmp::Ordering::Less,
266 (Some(_), None) => return cmp::Ordering::Greater,
267 (None, None) => return cmp::Ordering::Equal
268 }
269 }
270 }
271}
272
273impl<'a, N: ToRelativeDname + 'a> ToRelativeDname for &'a N { }
274