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