1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! Basic resource data handling.
//!
//! DNS resource records consist of some common data defining the domain
//! name they pertain to, their type and class, and finally record data
//! the format of which depends on the specific record type. As there are
//! currently more than eighty record types, having a giant enum for record
//! data seemed like a bad idea. Instead, resource records are generic over
//! two traits defined by this module. All concrete types implement
//! [`RecordData`]. Types that can be parsed out of messages also implement
//! [`ParsedRecordData`]. This distinction is only relevant for types that
//! contain and are generic over domain names: for these, parsing is only
//! available if the names use [`ParsedDName`].
//!
//! All concrete types shipped with this crate are implemented in the
//! [`domain::rdata`] module.
//!
//! In order to walk over all resource records in a message or work with
//! unknown record types, this module also defines the [`GenericRecordData`]
//! type that can deal with all record types but provides only a limited
//! functionality.
//!
//! [`RecordData`]: trait.RecordData.html
//! [`ParsedRecordData`]: trait.ParsedRecordData.html
//! [`domain::rdata`]: ../../rdata/index.html
//! [`GenericRecordData`]: struct.GenericRecordData.html

use std::fmt;
use ::iana::Rtype;
use ::rdata::fmt_rdata;
use super::{Composer, ComposeResult, Parser, ParseResult};


//----------- RecordData -----------------------------------------------------

/// A trait for types representing record data.
pub trait RecordData: Sized {
    /// Returns the record type for this record data instance.
    ///
    /// This is a method rather than an associated function to allow one
    /// type to be used for several real record types.
    fn rtype(&self) -> Rtype;

    /// Appends the record data to the end of a composer.
    fn compose<C: AsMut<Composer>>(&self, target: C) -> ComposeResult<()>;
}


//------------ ParsedRecordData ----------------------------------------------

/// A trait for types that allow parsing record data from a message.
pub trait ParsedRecordData<'a>: RecordData {
    /// Parses the record data out of a parser.
    ///
    /// The `parser` handed into the function will be limited to the length
    /// of the record data, so can read until the end of the parser.
    fn parse(rtype: Rtype, parser: &mut Parser<'a>)
             -> ParseResult<Option<Self>>;
}


//------------ GenericRecordData --------------------------------------------

/// A type for parsing any type of record data.
///
/// This type accepts any record type and stores a reference to the plain
/// binary record data in the message. This way, it can later be converted
/// into concrete record data if necessary via the [`reparse()`] method.
///
/// Because the data referenced by a value may contain compressed domain
/// names, transitively building a new message from this data may lead to
/// corrupt messages. To avoid this sort of thing, 
/// [RFC 3597], ‘Handling of Unknown DNS Resource Record (RR) Types,’
/// restricted compressed domain names to record types defined in [RFC 1035].
/// Accordingly, this types [`RecordData::compose()`] implementation treats
/// these types specially and ensures that their names are handles correctly.
/// This may still lead to corrupt messages, however, if the generic record
/// data is obtained from a source not complying with RFC 3597. In general,
/// be wary when re-composing parsed messages unseen.
///
/// [`RecordData::compose()`]: trait.RecordData.html#tymethod.compose
/// [RFC 1035]: https://tools.ietf.org/html/rfc1035
/// [RFC 3597]: https://tools.ietf.org/html/rfc3597
#[derive(Clone, Debug)]
pub struct GenericRecordData<'a> {
    /// The record type of this data.
    rtype: Rtype,

    /// A parser for the record’s data.
    ///
    /// The parser will be positioned at the beginning of the record data and
    /// will be limited to the length of the record data.
    parser: Parser<'a>,
}

impl<'a> GenericRecordData<'a> {
    /// Tries to re-parse the data for the given record data type.
    ///
    /// # Panics
    ///
    /// This method panics if the specified record data type does not
    /// actually feel like parsing data of the value’s record type.
    fn reparse<D: ParsedRecordData<'a>>(&self) -> ParseResult<D> {
        D::parse(self.rtype, &mut self.parser.clone()).map(Option::unwrap)
    }
}

impl<'a> RecordData for GenericRecordData<'a> {
    fn rtype(&self) -> Rtype {
        self.rtype
    }

    fn compose<C: AsMut<Composer>>(&self, mut target: C)
                        -> ComposeResult<()> {
        use ::rdata::rfc1035::parsed::*;

        match self.rtype {
            // Special treatment for any type from RFC 1035 that contains
            // domain names.
            Rtype::Cname => try!(self.reparse::<Cname>()).compose(target),
            Rtype::Mb => try!(self.reparse::<Mb>()).compose(target),
            Rtype::Md => try!(self.reparse::<Md>()).compose(target),
            Rtype::Mf => try!(self.reparse::<Mf>()).compose(target),
            Rtype::Mg => try!(self.reparse::<Mg>()).compose(target),
            Rtype::Minfo => try!(self.reparse::<Minfo>()).compose(target),
            Rtype::Mr => try!(self.reparse::<Mr>()).compose(target),
            Rtype::Mx => try!(self.reparse::<Mx>()).compose(target),
            Rtype::Ns => try!(self.reparse::<Ns>()).compose(target),
            Rtype::Ptr => try!(self.reparse::<Ptr>()).compose(target),
            Rtype::Soa => try!(self.reparse::<Soa>()).compose(target),

            // Anything else can go verbatim.
            _ => {
                let len = self.parser.remaining();
                let bytes = try!(self.parser.clone().parse_bytes(len));
                target.as_mut().compose_bytes(bytes)
            }
        }
    }
}

impl<'a> ParsedRecordData<'a> for GenericRecordData<'a> {
    fn parse(rtype: Rtype, parser: &mut Parser<'a>)
             -> ParseResult<Option<Self>> {
        let my_parser = parser.clone();
        let len = parser.remaining();
        try!(parser.skip(len));
        Ok(Some(GenericRecordData {
            rtype: rtype,
            parser: my_parser
        }))
    }
}

impl<'a> fmt::Display for GenericRecordData<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt_rdata(self.rtype, &mut self.parser.clone(), f)
    }
}