domain_core/bits/name/
uncertain.rs

1//! A domain name that can be both relative or absolute.
2//!
3//! This is a private module. Its public types are re-exported by the parent.
4
5use std::{fmt, str};
6use bytes::BufMut;
7use ::bits::compose::Compose;
8use ::master::scan::{CharSource, Scan, Scanner, ScanError, Symbol};
9use super::builder::{DnameBuilder, PushError, PushNameError};
10use super::chain::{Chain, LongChainError};
11use super::dname::Dname;
12use super::relative::{DnameIter, RelativeDname};
13use super::traits::{ToDname, ToLabelIter};
14
15
16//------------ UncertainDname ------------------------------------------------
17
18/// A domain name that may be absolute or relative.
19///
20/// This type is helpful when reading a domain name from some source where it
21/// may end up being absolute or not.
22#[derive(Clone, Eq, Hash, PartialEq)]
23pub enum UncertainDname {
24    Absolute(Dname),
25    Relative(RelativeDname),
26}
27
28impl UncertainDname {
29    /// Creates a new uncertain domain name from an absolute domain name.
30    pub fn absolute(name: Dname) -> Self {
31        UncertainDname::Absolute(name)
32    }
33
34    /// Creates a new uncertain domain name from a relative domain name.
35    pub fn relative(name: RelativeDname) -> Self {
36        UncertainDname::Relative(name)
37    }
38
39    /// Creates a new uncertain domain name containing the root label only.
40    pub fn root() -> Self {
41        UncertainDname::Absolute(Dname::root())
42    }
43
44    /// Creates a new uncertain yet empty domain name.
45    pub fn empty() -> Self {
46        UncertainDname::Relative(RelativeDname::empty())
47    }
48
49    /// Creates a domain name from a sequence of characters.
50    ///
51    /// The sequence must result in a domain name in master format
52    /// representation. That is, its labels should be separated by dots,
53    /// actual dots, white space and backslashes should be escaped by a
54    /// preceeding backslash, and any byte value that is not a printable
55    /// ASCII character should be encoded by a backslash followed by its
56    /// three digit decimal value.
57    ///
58    /// If the last character is a dot, the name will be absolute, otherwise
59    /// it will be relative.
60    ///
61    /// If you have a string, you can also use the `FromStr` trait, which
62    /// really does the same thing.
63    pub fn from_chars<C>(chars: C) -> Result<Self, FromStrError>
64                      where C: IntoIterator<Item=char> {
65        Self::_from_chars(chars.into_iter(), DnameBuilder::new())
66    }
67
68    /// Does the actual work for `from_chars` and `FromStr::from_str`.
69    fn _from_chars<C>(mut chars: C, mut target: DnameBuilder)
70                      -> Result<Self, FromStrError>
71                   where C: Iterator<Item=char> {
72        while let Some(ch) = chars.next() {
73            match ch {
74                '.' => {
75                    if !target.in_label() {
76                        return Err(FromStrError::EmptyLabel)
77                    }
78                    target.end_label();
79                }
80                '\\' => {
81                    let in_label = target.in_label();
82                    target.push(parse_escape(&mut chars, in_label)?)?;
83                }
84                ' ' ... '-' | '/' ... '[' | ']' ... '~' => {
85                    target.push(ch as u8)?
86                }
87                _ => return Err(FromStrError::IllegalCharacter(ch))
88            }
89        }
90        if target.in_label() || target.is_empty() {
91            Ok(target.finish().into())
92        }
93        else {
94            target.into_dname().map(Into::into)
95                               .map_err(|_| FromStrError::LongName)
96        }
97    }
98
99    /// Returns whether the name is absolute.
100    pub fn is_absolute(&self) -> bool {
101        match *self {
102            UncertainDname::Absolute(_) => true,
103            UncertainDname::Relative(_) => false,
104        }
105    }
106
107    /// Returns whether the name is relative.
108    pub fn is_relative(&self) -> bool {
109        !self.is_absolute()
110    }
111
112    /// Returns a reference to an absolute name, if this name is absolute.
113    pub fn as_absolute(&self) -> Option<&Dname> {
114        match *self {
115            UncertainDname::Absolute(ref name) => Some(name),
116            _ => None
117        }
118    }
119
120    /// Returns a reference to a relative name, if the name is relative.
121    pub fn as_relative(&self) -> Option<&RelativeDname> {
122        match *self {
123            UncertainDname::Relative(ref name) => Some(name),
124            _ => None,
125        }
126    }
127
128    /// Converts the name into an absolute name if it is absolute.
129    ///
130    /// Otherwise, returns itself as the error.
131    pub fn try_into_absolute(self) -> Result<Dname, Self> {
132        if let UncertainDname::Absolute(name) = self {
133            Ok(name)
134        }
135        else {
136            Err(self)
137        }
138    }
139
140    /// Converts the name into a relative name if it is relative.
141    ///
142    /// Otherwise just returns itself as the error.
143    pub fn try_into_relative(self) -> Result<RelativeDname, Self> {
144        if let UncertainDname::Relative(name) = self {
145            Ok(name)
146        }
147        else {
148            Err(self)
149        }
150    }
151
152    /// Converts the name into an absolute name.
153    ///
154    /// If the name is relative, appends the root label to it using
155    /// [`RelativeDname::into_absolute`].
156    ///
157    /// [`RelativeDname::into_absolute`]:
158    ///     struct.RelativeDname.html#method.into_absolute
159    pub fn into_absolute(self) -> Dname {
160        match self {
161            UncertainDname::Absolute(name) => name,
162            UncertainDname::Relative(name) => name.into_absolute()
163        }
164    }
165
166    /// Makes an uncertain name absolute by chaining on a suffix if needed.
167    ///
168    /// The method converts the uncertain name into a chain that will
169    /// be absolute. If the name is already absolute, the chain will be the
170    /// name itself. If it is relative, if will be the concatenation of the
171    /// name and `suffix`.
172    pub fn chain<S: ToDname>(self, suffix: S)
173                             -> Result<Chain<Self, S>, LongChainError> {
174        Chain::new_uncertain(self, suffix)
175    }
176
177    /// Returns a byte slice with the raw content of the name.
178    pub fn as_slice(&self) -> &[u8] {
179        match *self {
180            UncertainDname::Absolute(ref name) => name.as_slice(),
181            UncertainDname::Relative(ref name) => name.as_slice(),
182        }
183    }
184}
185
186
187//--- Compose
188
189impl Compose for UncertainDname {
190    fn compose_len(&self) -> usize {
191        match *self {
192            UncertainDname::Absolute(ref name) => name.compose_len(),
193            UncertainDname::Relative(ref name) => name.compose_len(),
194        }
195    }
196
197    fn compose<B: BufMut>(&self, buf: &mut B) {
198        match *self {
199            UncertainDname::Absolute(ref name) => name.compose(buf),
200            UncertainDname::Relative(ref name) => name.compose(buf),
201        }
202    }
203}
204
205
206//--- From
207
208impl From<Dname> for UncertainDname {
209    fn from(name: Dname) -> Self {
210        Self::absolute(name)
211    }
212}
213
214impl From<RelativeDname> for UncertainDname {
215    fn from(name: RelativeDname) -> Self {
216        Self::relative(name)
217    }
218}
219
220
221//--- FromStr
222
223impl str::FromStr for UncertainDname {
224    type Err = FromStrError;
225
226    fn from_str(s: &str) -> Result<Self, Self::Err> {
227        Self::_from_chars(s.chars(), DnameBuilder::with_capacity(s.len()))
228    }
229}
230
231
232//--- Scan
233
234impl Scan for UncertainDname {
235    fn scan<C: CharSource>(scanner: &mut Scanner<C>)
236                           -> Result<Self, ScanError> {
237        if let Ok(()) = scanner.skip_literal(".") {
238            return Ok(UncertainDname::root())
239        }
240        scanner.scan_word(
241            DnameBuilder::new(),
242            |name, symbol| {
243                match symbol {
244                    Symbol::Char('.') => {
245                        if name.in_label() {
246                            name.end_label();
247                        }
248                        else {
249                            return Err(FromStrError::EmptyLabel.into())
250                        }
251                    }
252                    Symbol::Char(ch) | Symbol::SimpleEscape(ch) => {
253                        if ch.is_ascii() {
254                            if let Err(err) = name.push(ch as u8) {
255                                return Err(FromStrError::from(err).into())
256                            }
257                        }
258                        else {
259                            return Err(FromStrError::IllegalCharacter(ch)
260                                                    .into())
261                        }
262                    }
263                    Symbol::DecimalEscape(ch) => {
264                        if let Err(err) = name.push(ch) {
265                            return Err(FromStrError::from(err).into())
266                        }
267                    }
268                }
269                Ok(())
270            },
271            |name| {
272                if name.in_label() || name.is_empty() {
273                    Ok(name.finish().into())
274                }
275                else {
276                    name.into_dname()
277                        .map(Into::into)
278                        .map_err(|err| FromStrError::from(err).into())
279                }
280            }
281        )
282    }
283}
284
285
286//--- ToLabelIter
287
288impl<'a> ToLabelIter<'a> for UncertainDname {
289    type LabelIter = DnameIter<'a>;
290
291    fn iter_labels(&'a self) -> Self::LabelIter {
292        match *self {
293            UncertainDname::Absolute(ref name) => name.iter_labels(),
294            UncertainDname::Relative(ref name) => name.iter_labels(),
295        }
296    }
297}
298
299
300//--- Display and Debug
301
302impl fmt::Display for UncertainDname {
303    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304        match *self {
305            UncertainDname::Absolute(ref name) => name.fmt(f),
306            UncertainDname::Relative(ref name) => name.fmt(f),
307        }
308    }
309}
310
311impl fmt::Debug for UncertainDname {
312    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
313        match *self {
314            UncertainDname::Absolute(ref name) => {
315                write!(f, "UncertainDname::Absolute({})", name)
316            }
317            UncertainDname::Relative(ref name) => {
318                write!(f, "UncertainDname::Relative({})", name)
319            }
320        }
321    }
322}
323
324
325//------------ Santa’s Little Helpers ----------------------------------------
326
327/// Parses the contents of an escape sequence from `chars`.
328///
329/// The backslash should already have been taken out of `chars`.
330fn parse_escape<C>(chars: &mut C, in_label: bool) -> Result<u8, FromStrError>
331                where C: Iterator<Item=char> {
332    let ch = try!(chars.next().ok_or(FromStrError::UnexpectedEnd));
333    if ch >= '0' &&  ch <= '9' {
334        let v = ch.to_digit(10).unwrap() * 100
335              + try!(chars.next().ok_or(FromStrError::UnexpectedEnd)
336                     .and_then(|c| c.to_digit(10)
337                                    .ok_or(FromStrError::IllegalEscape)))
338                     * 10
339              + try!(chars.next().ok_or(FromStrError::UnexpectedEnd)
340                     .and_then(|c| c.to_digit(10)
341                                    .ok_or(FromStrError::IllegalEscape)));
342        if v > 255 {
343            return Err(FromStrError::IllegalEscape)
344        }
345        Ok(v as u8)
346    }
347    else if ch == '[' {
348        // `\[` at the start of a label marks a binary label which we don’t
349        // support. Within a label, the sequence is fine.
350        if in_label {
351            Ok(b'[')
352        }
353        else {
354            Err(FromStrError::BinaryLabel)
355        }
356    }
357    else { Ok(ch as u8) }
358}
359
360
361//------------ FromStrError --------------------------------------------------
362
363#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
364pub enum FromStrError {
365    /// The string ended when there should have been more characters.
366    ///
367    /// This most likely happens inside escape sequences and quoting.
368    #[fail(display="unexpected end of input")]
369    UnexpectedEnd,
370
371    /// An empty label was encountered.
372    #[fail(display="an empty label was encountered")]
373    EmptyLabel,
374
375    /// A binary label was encountered.
376    #[fail(display="a binary label was encountered")]
377    BinaryLabel,
378
379    /// A domain name label has more than 63 octets.
380    #[fail(display="label length limit exceeded")]
381    LongLabel,
382
383    /// An illegal escape sequence was encountered.
384    ///
385    /// Escape sequences are a backslash character followed by either a
386    /// three decimal digit sequence encoding a byte value or a single
387    /// other printable ASCII character.
388    #[fail(display="illegal escape sequence")]
389    IllegalEscape,
390
391    /// An illegal character was encountered.
392    ///
393    /// Only printable ASCII characters are allowed.
394    #[fail(display="illegal character '{}'", _0)]
395    IllegalCharacter(char),
396
397    /// The name has more than 255 characters.
398    #[fail(display="long domain name")]
399    LongName,
400}
401
402impl From<PushError> for FromStrError {
403    fn from(err: PushError) -> FromStrError {
404        match err {
405            PushError::LongLabel => FromStrError::LongLabel,
406            PushError::LongName => FromStrError::LongName,
407        }
408    }
409}
410
411impl From<PushNameError> for FromStrError {
412    fn from(_: PushNameError) -> FromStrError {
413        FromStrError::LongName
414    }
415}
416
417
418//============ Testing =======================================================
419
420#[cfg(test)]
421mod test {
422    use super::*;
423
424    #[test]
425    fn from_str() {
426        use std::str::FromStr;
427
428        fn name(s: &str) -> UncertainDname {
429            UncertainDname::from_str(s).unwrap()
430        }
431
432        assert_eq!(name("www.example.com").as_relative().unwrap().as_slice(),
433                   b"\x03www\x07example\x03com");
434        assert_eq!(name("www.example.com.").as_absolute().unwrap().as_slice(),
435                   b"\x03www\x07example\x03com\0");
436
437        assert_eq!(name(r"www\.example.com").as_slice(),
438                   b"\x0bwww.example\x03com");
439        assert_eq!(name(r"w\119w.example.com").as_slice(),
440                   b"\x03www\x07example\x03com");
441        assert_eq!(name(r"w\000w.example.com").as_slice(),
442                   b"\x03w\0w\x07example\x03com");
443
444        assert_eq!(UncertainDname::from_str(r"w\01"),
445                   Err(FromStrError::UnexpectedEnd));
446        assert_eq!(UncertainDname::from_str(r"w\"),
447                   Err(FromStrError::UnexpectedEnd));
448        assert_eq!(UncertainDname::from_str(r"www..example.com"),
449                   Err(FromStrError::EmptyLabel));
450        assert_eq!(UncertainDname::from_str(r"www.example.com.."),
451                   Err(FromStrError::EmptyLabel));
452        assert_eq!(UncertainDname::from_str(r".www.example.com"),
453                   Err(FromStrError::EmptyLabel));
454        assert_eq!(UncertainDname::from_str(r"www.\[322].example.com"),
455                   Err(FromStrError::BinaryLabel));
456        assert_eq!(UncertainDname::from_str(r"www.\2example.com"),
457                   Err(FromStrError::IllegalEscape));
458        assert_eq!(UncertainDname::from_str(r"www.\29example.com"),
459                   Err(FromStrError::IllegalEscape));
460        assert_eq!(UncertainDname::from_str(r"www.\299example.com"),
461                   Err(FromStrError::IllegalEscape));
462        assert_eq!(UncertainDname::from_str(r"www.\892example.com"),
463                   Err(FromStrError::IllegalEscape));
464        assert_eq!(UncertainDname::from_str("www.e\0ample.com"),
465                   Err(FromStrError::IllegalCharacter('\0')));
466        assert_eq!(UncertainDname::from_str("www.eüample.com"),
467                   Err(FromStrError::IllegalCharacter('ü')));
468
469        // LongLabel
470        let mut s = String::from("www.");
471        for _ in 0..63 {
472            s.push('x');
473        }
474        s.push_str(".com");
475        assert!(UncertainDname::from_str(&s).is_ok());
476        let mut s = String::from("www.");
477        for _ in 0..64 {
478            s.push('x');
479        }
480        s.push_str(".com");
481        assert_eq!(UncertainDname::from_str(&s),
482                   Err(FromStrError::LongLabel));
483
484        // Long Name
485        let mut s = String::new();
486        for _ in 0..50 {
487            s.push_str("four.");
488        }
489        let mut s1 = s.clone();
490        s1.push_str("com.");
491        assert_eq!(name(&s1).as_slice().len(), 255);
492        let mut s1 = s.clone();
493        s1.push_str("com");
494        assert_eq!(name(&s1).as_slice().len(), 254);
495        let mut s1 = s.clone();
496        s1.push_str("coma.");
497        assert_eq!(UncertainDname::from_str(&s1), Err(FromStrError::LongName));
498        let mut s1 = s.clone();
499        s1.push_str("coma");
500        assert_eq!(UncertainDname::from_str(&s1), Err(FromStrError::LongName));
501    }
502}