Skip to main content

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