hickory_proto/rr/lower_name.rs
1// Copyright 2015-2019 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! domain name, aka labels, implementation
9
10use std::borrow::Borrow;
11use std::cmp::{Ordering, PartialEq};
12use std::fmt;
13use std::hash::{Hash, Hasher};
14use std::str::FromStr;
15
16use crate::error::*;
17#[cfg(feature = "serde-config")]
18use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
19
20use crate::rr::Name;
21use crate::serialize::binary::*;
22
23/// TODO: all LowerNames should be stored in a global "intern" space, and then everything that uses
24/// them should be through references. As a workaround the Strings are all Rc as well as the array
25#[derive(Default, Debug, Eq, Clone)]
26pub struct LowerName(Name);
27
28impl LowerName {
29 /// Create a new domain::LowerName, i.e. label
30 pub fn new(name: &Name) -> Self {
31 Self(name.to_lowercase())
32 }
33
34 /// Returns true if there are no labels, i.e. it's empty.
35 ///
36 /// In DNS the root is represented by `.`
37 ///
38 /// # Examples
39 ///
40 /// ```
41 /// use hickory_proto::rr::{LowerName, Name};
42 ///
43 /// let root = LowerName::from(Name::root());
44 /// assert_eq!(&root.to_string(), ".");
45 /// ```
46 pub fn is_root(&self) -> bool {
47 self.0.is_root()
48 }
49
50 /// Returns true if the name is a fully qualified domain name.
51 ///
52 /// If this is true, it has effects like only querying for this single name, as opposed to building
53 /// up a search list in resolvers.
54 ///
55 /// *warning: this interface is unstable and may change in the future*
56 ///
57 /// # Examples
58 ///
59 /// ```
60 /// use std::str::FromStr;
61 /// use hickory_proto::rr::{LowerName, Name};
62 ///
63 /// let name = LowerName::from(Name::from_str("www").unwrap());
64 /// assert!(!name.is_fqdn());
65 ///
66 /// let name = LowerName::from(Name::from_str("www.example.com").unwrap());
67 /// assert!(!name.is_fqdn());
68 ///
69 /// let name = LowerName::from(Name::from_str("www.example.com.").unwrap());
70 /// assert!(name.is_fqdn());
71 /// ```
72 pub fn is_fqdn(&self) -> bool {
73 self.0.is_fqdn()
74 }
75
76 /// Trims off the first part of the name, to help with searching for the domain piece
77 ///
78 /// # Examples
79 ///
80 /// ```
81 /// use std::str::FromStr;
82 /// use hickory_proto::rr::{LowerName, Name};
83 ///
84 /// let example_com = LowerName::from(Name::from_str("example.com").unwrap());
85 /// assert_eq!(example_com.base_name(), LowerName::from(Name::from_str("com.").unwrap()));
86 /// assert_eq!(LowerName::from(Name::from_str("com.").unwrap().base_name()), LowerName::from(Name::root()));
87 /// assert_eq!(LowerName::from(Name::root().base_name()), LowerName::from(Name::root()));
88 /// ```
89 pub fn base_name(&self) -> Self {
90 Self(self.0.base_name())
91 }
92
93 /// returns true if the name components of self are all present at the end of name
94 ///
95 /// # Example
96 ///
97 /// ```rust
98 /// use std::str::FromStr;
99 /// use hickory_proto::rr::{LowerName, Name};
100 ///
101 /// let name = LowerName::from(Name::from_str("www.example.com").unwrap());
102 /// let zone = LowerName::from(Name::from_str("example.com").unwrap());
103 /// let another = LowerName::from(Name::from_str("example.net").unwrap());
104 /// assert!(zone.zone_of(&name));
105 /// assert!(!another.zone_of(&name));
106 /// ```
107 pub fn zone_of(&self, name: &Self) -> bool {
108 self.0.zone_of_case(&name.0)
109 }
110
111 /// Returns the number of labels in the name, discounting `*`.
112 ///
113 /// # Examples
114 ///
115 /// ```
116 /// use std::str::FromStr;
117 /// use hickory_proto::rr::{LowerName, Name};
118 ///
119 /// let root = LowerName::from(Name::root());
120 /// assert_eq!(root.num_labels(), 0);
121 ///
122 /// let example_com = LowerName::from(Name::from_str("example.com").unwrap());
123 /// assert_eq!(example_com.num_labels(), 2);
124 ///
125 /// let star_example_com = LowerName::from(Name::from_str("*.example.com").unwrap());
126 /// assert_eq!(star_example_com.num_labels(), 2);
127 /// ```
128 pub fn num_labels(&self) -> u8 {
129 self.0.num_labels()
130 }
131
132 /// returns the length in bytes of the labels. '.' counts as 1
133 ///
134 /// This can be used as an estimate, when serializing labels, they will often be compressed
135 /// and/or escaped causing the exact length to be different.
136 pub fn len(&self) -> usize {
137 self.0.len()
138 }
139
140 /// Returns true if the name is empty
141 pub fn is_empty(&self) -> bool {
142 self.0.is_empty()
143 }
144
145 /// Emits the canonical version of the name to the encoder.
146 ///
147 /// In canonical form, there will be no pointers written to the encoder (i.e. no compression).
148 pub fn emit_as_canonical(
149 &self,
150 encoder: &mut BinEncoder<'_>,
151 canonical: bool,
152 ) -> ProtoResult<()> {
153 self.0.emit_as_canonical(encoder, canonical)
154 }
155
156 /// Pass through for Name::is_wildcard
157 pub fn is_wildcard(&self) -> bool {
158 self.0.is_wildcard()
159 }
160
161 /// Replaces the first label with the wildcard character, "*"
162 pub fn into_wildcard(self) -> Self {
163 let name = self.0.into_wildcard();
164 Self(name)
165 }
166}
167
168impl Hash for LowerName {
169 fn hash<H>(&self, state: &mut H)
170 where
171 H: Hasher,
172 {
173 for label in &self.0 {
174 state.write(label);
175 }
176 }
177}
178
179impl PartialEq<Self> for LowerName {
180 fn eq(&self, other: &Self) -> bool {
181 self.0.eq_case(&other.0)
182 }
183}
184
185impl BinEncodable for LowerName {
186 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
187 let is_canonical_names = encoder.is_canonical_names();
188 self.emit_as_canonical(encoder, is_canonical_names)
189 }
190}
191
192impl fmt::Display for LowerName {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 self.0.fmt(f)
195 }
196}
197
198impl PartialOrd<Self> for LowerName {
199 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
200 Some(self.cmp(other))
201 }
202}
203
204impl Ord for LowerName {
205 /// Given two lower cased names, this performs a case sensitive comparison.
206 ///
207 /// ```text
208 /// RFC 4034 DNSSEC Resource Records March 2005
209 ///
210 /// 6.1. Canonical DNS LowerName Order
211 ///
212 /// For the purposes of DNS security, owner names are ordered by treating
213 /// individual labels as unsigned left-justified octet strings. The
214 /// absence of a octet sorts before a zero value octet, and uppercase
215 /// US-ASCII letters are treated as if they were lowercase US-ASCII
216 /// letters.
217 ///
218 /// To compute the canonical ordering of a set of DNS names, start by
219 /// sorting the names according to their most significant (rightmost)
220 /// labels. For names in which the most significant label is identical,
221 /// continue sorting according to their next most significant label, and
222 /// so forth.
223 ///
224 /// For example, the following names are sorted in canonical DNS name
225 /// order. The most significant label is "example". At this level,
226 /// "example" sorts first, followed by names ending in "a.example", then
227 /// by names ending "z.example". The names within each level are sorted
228 /// in the same way.
229 ///
230 /// example
231 /// a.example
232 /// yljkjljk.a.example
233 /// Z.a.example
234 /// zABC.a.EXAMPLE
235 /// z.example
236 /// \001.z.example
237 /// *.z.example
238 /// \200.z.example
239 /// ```
240 fn cmp(&self, other: &Self) -> Ordering {
241 self.0.cmp_case(&other.0)
242 }
243}
244
245impl From<Name> for LowerName {
246 fn from(name: Name) -> Self {
247 Self::new(&name)
248 }
249}
250
251impl<'a> From<&'a Name> for LowerName {
252 fn from(name: &'a Name) -> Self {
253 Self::new(name)
254 }
255}
256
257impl From<LowerName> for Name {
258 fn from(name: LowerName) -> Self {
259 name.0
260 }
261}
262
263impl<'a> From<&'a LowerName> for Name {
264 fn from(name: &'a LowerName) -> Self {
265 name.0.clone()
266 }
267}
268
269impl Borrow<Name> for LowerName {
270 fn borrow(&self) -> &Name {
271 &self.0
272 }
273}
274
275impl<'r> BinDecodable<'r> for LowerName {
276 /// parses the chain of labels
277 /// this has a max of 255 octets, with each label being less than 63.
278 /// all names will be stored lowercase internally.
279 /// This will consume the portions of the Vec which it is reading...
280 fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
281 let name = Name::read(decoder)?;
282 Ok(Self(name.to_lowercase()))
283 }
284}
285
286impl FromStr for LowerName {
287 type Err = ProtoError;
288
289 fn from_str(name: &str) -> Result<Self, Self::Err> {
290 Name::from_str(name).map(Self::from)
291 }
292}
293
294#[cfg(feature = "serde-config")]
295impl Serialize for LowerName {
296 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
297 where
298 S: Serializer,
299 {
300 serializer.serialize_str(&self.to_string())
301 }
302}
303
304#[cfg(feature = "serde-config")]
305impl<'de> Deserialize<'de> for LowerName {
306 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
307 where
308 D: Deserializer<'de>,
309 {
310 let s = String::deserialize(deserializer)?;
311 FromStr::from_str(&s).map_err(de::Error::custom)
312 }
313}