domain_core/bits/record.rs
1//! Resource Records.
2//!
3//! This module defines types related to DNS resource records. The most
4//! complete one is [`Record`] which contains a complete record for a certain
5//! record type. [`RecordHeader`] contains the data from a record’s header,
6//! the first couple of octets common to all records. Finally,
7//! [`ParsedRecord`] is similar to [`Record`] but contains the record data
8//! in its raw, encoded form.
9//!
10//! [`Record`]: struct.Record.html
11//! [`RecordHeader`]: struct.RecordHeader.html
12//! [`ParsedRecord`]: struct.ParsedRecord.html
13
14use std::fmt;
15use bytes::{BigEndian, BufMut, ByteOrder};
16use failure::Fail;
17use ::iana::{Class, Rtype};
18use super::compose::{Compose, Compress, Compressor};
19use super::name::{ParsedDname, ParsedDnameError, ToDname};
20use super::parse::{Parse, Parser, ShortBuf};
21use super::rdata::{ParseRecordData, RecordData};
22
23
24//------------ Record --------------------------------------------------------
25
26/// A DNS resource record.
27///
28/// All information available through the DNS is stored in resource records.
29/// They have a three part key of a domain name, resource record type, and
30/// class. Data is arranged in a tree which is navigated using the domain
31/// name. Each node in the tree carries a label, starting with the root
32/// label as the top-most node. The tree is traversed by stepping through the
33/// name from right to left, finding a child node carring the label of each
34/// step. The domain name resulting from this traversal is part of the
35/// record itself. It is called the *owner* of the record.
36///
37/// The record type describes the kind of data the record holds, such as IP
38/// addresses. The class, finally, describes which sort of network the
39/// information is for since DNS was originally intended to be used for
40/// networks other than the Internet as well. In practice, the only relevant
41/// class is IN, the Internet. Note that each class has its own tree of nodes.
42///
43/// The payload of a resource record is its data. Its purpose, meaning, and
44/// format is determined by the record type (technically, also its class).
45/// For each unique three-part key there can be multiple resource records.
46/// All these records for the same key are called *resource record sets,*
47/// most often shortened to ‘RRset.’
48///
49/// There is one more piece of data: the TTL or time to live. This value
50/// says how long a record remains valid before it should be refreshed from
51/// its original source, given in seconds. The TTL is used to add caching
52/// facilities to the DNS.
53///
54/// Values of the `Record` type represent one single resource record. Since
55/// there are currently more than eighty record types—see [`Rtype`] for a
56/// complete list—, the type is generic over a trait for record data. This
57/// trait holds both the record type value and the record data as they are
58/// inseparably entwined.
59///
60/// Because a record’s owner is a domain name, the `Record` type is
61/// additionally generic over the domain name type is for it. 
62///
63/// There is three ways to create a record value. First, you can make one
64/// yourself using the [`new`] function. It will neatly take care of all
65/// the generics through type inference. Secondly, you can parse a record
66/// from an existing message. [`Message`] and its friends provide a way to
67/// do that; see there for all the details. Finally, you can scan a record
68/// from master data (aka zonefiles). See the [`domain::master`] module for
69/// that.
70///
71/// Records can be place into DNS messages by using a [`MessageBuilder`]. In
72/// order to make adding records easier, `Record` implements the `From` trait
73/// for two kinds of tuples: A four-tuple of owner, class, time-to-live value,
74/// and record data and a triple leaving out the class and assuming it to be
75/// `Class::In`.
76///
77/// [`new`]: #method.new
78/// [`Message`]: ../message/struct.Message.html
79/// [`MessageBuilder`]: ../message_builder/struct.MessageBuilder.html
80/// [`Rtype`]: ../../iana/enum.Rtype.html
81/// [`domain::master`]: ../../master/index.html
82#[derive(Clone, Debug)]
83pub struct Record<N: ToDname, D: RecordData> {
84    /// The owner of the record.
85    owner: N,
86
87    /// The class of the record.
88    class: Class,
89
90    /// The time-to-live value of the record.
91    ttl: u32,
92
93    /// The record data. The value also specifies the record’s type.
94    data: D
95}
96
97
98/// # Creation and Element Access
99///
100impl<N: ToDname, D: RecordData> Record<N, D> {
101    /// Creates a new record from its parts.
102    pub fn new(owner: N, class: Class, ttl: u32, data: D) -> Self {
103        Record { owner, class, ttl, data }
104    }
105
106    /// Returns a reference to owner domain name.
107    ///
108    /// The owner of a record is the domain name that specifies the node in
109    /// the DNS tree this record belongs to.
110    pub fn owner(&self) -> &N {
111        &self.owner
112    }
113
114    /// Returns the record type.
115    pub fn rtype(&self) -> Rtype where D: RecordData {
116        self.data.rtype()
117    }
118
119    /// Returns the record class.
120    pub fn class(&self) -> Class {
121        self.class
122    }
123
124    /// Sets the record’s class.
125    pub fn set_class(&mut self, class: Class) {
126        self.class = class
127    }
128
129    /// Returns the record’s time-to-live.
130    pub fn ttl(&self) -> u32 {
131        self.ttl
132    }
133
134    /// Sets the record’s time-to-live.
135    pub fn set_ttl(&mut self, ttl: u32) {
136        self.ttl = ttl
137    }
138
139    /// Return a reference to the record data.
140    pub fn data(&self) -> &D {
141        &self.data
142    }
143
144    /// Returns a mutable reference to the record data.
145    pub fn data_mut(&mut self) -> &mut D {
146        &mut self.data
147    }
148
149    /// Trades the record for its record data.
150    pub fn into_data(self) -> D {
151        self.data
152    }
153}
154
155
156//--- From
157
158impl<N: ToDname, D: RecordData> From<(N, Class, u32, D)> for Record<N, D> {
159    fn from((owner, class, ttl, data): (N, Class, u32, D)) -> Self {
160        Self::new(owner, class, ttl, data)
161    }
162}
163
164impl<N: ToDname, D: RecordData> From<(N, u32, D)> for Record<N, D> {
165    fn from((owner, ttl, data): (N, u32, D)) -> Self {
166        Self::new(owner, Class::In, ttl, data)
167    }
168}
169
170
171//--- Parsable, Compose, and Compressor
172
173impl<D: ParseRecordData> Parse for Option<Record<ParsedDname, D>> {
174    type Err = RecordParseError<ParsedDnameError, D::Err>;
175
176    fn parse(parser: &mut Parser) -> Result<Self, Self::Err> {
177        let header = match RecordHeader::parse(parser) {
178            Ok(header) => header,
179            Err(err) => return Err(RecordParseError::Name(err)),
180        };
181        match D::parse_data(header.rtype(), parser, header.rdlen() as usize) {
182            Ok(Some(data)) => {
183                Ok(Some(header.into_record(data)))
184            }
185            Ok(None) => {
186                parser.advance(header.rdlen() as usize)?;
187                Ok(None)
188            }
189            Err(err) => {
190                Err(RecordParseError::Data(err))
191            }
192        }
193    }
194
195    fn skip(parser: &mut Parser) -> Result<(), Self::Err> {
196        ParsedRecord::skip(parser)
197                     .map_err(RecordParseError::Name)
198    }
199}
200
201impl<N: ToDname, D: RecordData> Compose for Record<N, D> {
202    fn compose_len(&self) -> usize {
203        self.owner.compose_len() + self.data.compose_len() + 10
204    }
205
206    fn compose<B: BufMut>(&self, buf: &mut B) {
207        RecordHeader::new(&self.owner, self.data.rtype(), self.class, self.ttl,
208                          self.data.compose_len() as u16)
209                     .compose(buf);
210        self.data.compose(buf);
211    }
212}
213
214impl<N: ToDname, D: RecordData + Compress> Compress
215            for Record<N, D> {
216    fn compress(&self, buf: &mut Compressor) -> Result<(), ShortBuf> {
217        self.owner.compress(buf)?;
218        buf.compose(&self.rtype())?;
219        buf.compose(&self.class)?;
220        buf.compose(&self.ttl)?;
221        let pos = buf.len();
222        buf.compose(&0u16)?;
223        self.data.compress(buf)?;
224        let len = buf.len() - pos - 2;
225        assert!(len <= (::std::u16::MAX as usize));
226        BigEndian::write_u16(&mut buf.as_slice_mut()[pos..], len as u16);
227        Ok(())
228    }
229}
230
231
232//--- Display
233
234impl<N, D> fmt::Display for Record<N, D>
235     where N: ToDname + fmt::Display, D: RecordData + fmt::Display {
236   fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
237        write!(f, "{}. {} {} {} {}",
238               self.owner, self.ttl, self.class, self.data.rtype(),
239               self.data)
240    }
241}
242
243
244//------------ RecordHeader --------------------------------------------------
245
246/// The header of a resource record.
247///
248/// This type encapsulates the common header of a resource record. It consists
249/// of the owner, record type, class, TTL, and the length of the record data.
250/// It is effectively a helper type for dealing with resource records encoded
251/// in a DNS message.
252///
253/// See [`Record`] for more details about resource records.
254#[derive(Clone, Debug, Eq, PartialEq)]
255pub struct RecordHeader<N> {
256    owner: N,
257    rtype: Rtype,
258    class: Class,
259    ttl: u32,
260    rdlen: u16,
261}
262
263impl<N> RecordHeader<N> {
264    /// Creates a new record header from its components.
265    pub fn new(owner: N, rtype: Rtype, class: Class, ttl: u32, rdlen: u16)
266               -> Self {
267        RecordHeader { owner, rtype, class, ttl, rdlen }
268    }
269}
270
271impl RecordHeader<ParsedDname> {
272    /// Parses a record header and then skips over the data.
273    ///
274    /// If the function succeeds, the parser will be positioned right behind
275    /// the end of the record.
276    pub fn parse_and_skip(parser: &mut Parser)
277                          -> Result<Self, ParsedDnameError> {
278        let header = Self::parse(parser)?;
279        match parser.advance(header.rdlen() as usize) {
280            Ok(()) => Ok(header),
281            Err(_) => Err(ShortBuf.into()),
282        }
283    }
284
285    /// Parses the remainder of the record and returns it.
286    ///
287    /// The method assumes that the parsers is currently positioned right
288    /// after the end of the record header. If the record data type `D`
289    /// feels capable of parsing a record with a header of `self`, the
290    /// method will parse the data and return a full `Record<D>`. Otherwise,
291    /// it skips over the record data.
292    #[allow(type_complexity)] // I know ...
293    pub fn parse_into_record<D: ParseRecordData>(self, parser: &mut Parser)
294                             -> Result<Option<Record<ParsedDname, D>>,
295                                       RecordParseError<ParsedDnameError,
296                                                        D::Err>> {
297        let end = parser.pos() + self.rdlen as usize;
298        match D::parse_data(self.rtype, parser, self.rdlen as usize)
299                .map_err(RecordParseError::Data)? {
300            Some(data) => Ok(Some(self.into_record(data))),
301            None => {
302                parser.seek(end)?;
303                Ok(None)
304            }
305        }
306    }
307
308    /// Parses only the record length and skips over all the other fields.
309    fn parse_rdlen(parser: &mut Parser) -> Result<u16, ParsedDnameError> {
310        ParsedDname::skip(parser)?;
311        Rtype::skip(parser)?;
312        Class::skip(parser)?;
313        u32::skip(parser)?;
314        Ok(u16::parse(parser)?)
315    }
316}
317
318impl<N: ToDname> RecordHeader<N> {
319    /// Returns a reference to the owner of the record.
320    pub fn owner(&self) -> &N {
321        &self.owner
322    }
323
324    /// Returns the record type of the record.
325    pub fn rtype(&self) -> Rtype {
326        self.rtype
327    }
328
329    /// Returns the class of the record.
330    pub fn class(&self) -> Class {
331        self.class
332    }
333
334    /// Returns the TTL of the record.
335    pub fn ttl(&self) -> u32 {
336        self.ttl
337    }
338
339    /// Returns the data length of the record.
340    pub fn rdlen(&self) -> u16 {
341        self.rdlen
342    }
343
344    /// Converts the header into an actual record.
345    pub fn into_record<D: RecordData>(self, data: D) -> Record<N, D> {
346        Record::new(self.owner, self.class, self.ttl, data)
347    }
348}
349
350
351//--- Parse, Compose, and Compress
352
353impl Parse for RecordHeader<ParsedDname> {
354    type Err = ParsedDnameError;
355
356    fn parse(parser: &mut Parser) -> Result<Self, Self::Err> {
357        Ok(RecordHeader::new(
358                ParsedDname::parse(parser)?,
359                Rtype::parse(parser)?,
360                Class::parse(parser)?,
361                u32::parse(parser)?,
362                parser.parse_u16()?
363        ))
364    }
365
366    fn skip(parser: &mut Parser) -> Result<(), Self::Err> {
367        ParsedDname::skip(parser)?;
368        Rtype::skip(parser)?;
369        Class::skip(parser)?;
370        u32::skip(parser)?;
371        u16::skip(parser)?;
372        Ok(())
373    }
374}
375
376impl<N: Compose> Compose for RecordHeader<N> {
377    fn compose_len(&self) -> usize {
378        self.owner.compose_len() + 10
379    }
380
381    fn compose<B: BufMut>(&self, buf: &mut B) {
382        self.owner.compose(buf);
383        self.rtype.compose(buf);
384        self.class.compose(buf);
385        self.ttl.compose(buf);
386        self.rdlen.compose(buf);
387    }
388}
389
390impl<N: Compress> Compress for RecordHeader<N> {
391    fn compress(&self, buf: &mut Compressor) -> Result<(), ShortBuf> {
392        self.owner.compress(buf)?;
393        buf.compose(&self.rtype)?;
394        buf.compose(&self.class)?;
395        buf.compose(&self.ttl)?;
396        buf.compose(&self.rdlen)
397    }
398}
399
400
401//------------ ParsedRecord --------------------------------------------------
402
403/// A raw record parsed from a message.
404///
405/// A value of this type contains the record header and the raw record data.
406/// It is mainly used as an intermediary type when turning raw message data
407/// into [`Record`]s.
408///
409/// It allows access to the header only but can be traded for a real record
410/// of a specific type of [`ParseRecordData`] (i.e., some type that knowns
411/// how to parse record data) via the [`to_record`] and [`into_record`]
412/// methods.
413///
414/// [`Record`]: struct.Record.html
415/// [`ParseRecordData`]: ../rdata/trait.ParseRecordData.html
416/// [`to_record`]: #method.to_record
417/// [`into_record`]: #method.into_record
418#[derive(Clone, Debug)]
419pub struct ParsedRecord {
420    /// The record’s header.
421    header: RecordHeader<ParsedDname>,
422
423    /// A parser positioned at the beginning of the record’s data.
424    data: Parser,
425}
426
427impl ParsedRecord {
428    /// Creates a new parsed record from a header and the record data.
429    ///
430    /// The record data is provided via a parser that is positioned at the
431    /// first byte of the record data.
432    pub fn new(header: RecordHeader<ParsedDname>, data: Parser) -> Self {
433        ParsedRecord { header, data }
434    }
435
436    /// Returns a reference to the owner of the record.
437    pub fn owner(&self) -> &ParsedDname {
438        self.header.owner()
439    }
440
441    /// Returns the record type of the record.
442    pub fn rtype(&self) -> Rtype {
443        self.header.rtype()
444    }
445
446    /// Returns the class of the record.
447    pub fn class(&self) -> Class {
448        self.header.class()
449    }
450
451    /// Returns the TTL of the record.
452    pub fn ttl(&self) -> u32 {
453        self.header.ttl()
454    }
455
456    /// Returns the data length of the record.
457    pub fn rdlen(&self) -> u16 {
458        self.header.rdlen()
459    }
460}
461
462impl ParsedRecord {
463    /// Creates a real resource record from the parsed record.
464    ///
465    /// The method is generic over a type that knows how to parse record
466    /// data via the [`ParseRecordData`] trait. The record data is given to
467    /// this trait for parsing. If the trait feels capable of parsing this
468    /// type of record (as indicated by the record type) and parsing succeeds,
469    /// the method returns `Ok(Some(_))`. It returns `Ok(None)` if the trait
470    /// doesn’t know how to parse this particular record type. It returns
471    /// an error if parsing fails.
472    ///
473    /// [`ParseRecordData`]: ../rdata/trait.ParseRecordData.html
474    #[allow(type_complexity)] // I know ...
475    pub fn to_record<D>(
476        &self
477    ) -> Result<Option<Record<ParsedDname, D>>,
478                RecordParseError<ParsedDnameError, D::Err>>
479    where D: ParseRecordData
480    {
481        match D::parse_data(self.header.rtype(), &mut self.data.clone(),
482                            self.header.rdlen() as usize)
483                .map_err(RecordParseError::Data)? {
484            Some(data) => Ok(Some(self.header.clone().into_record(data))),
485            None => Ok(None)
486        }
487    }
488
489    /// Trades the parsed record for a real resource record.
490    ///
491    /// The method is generic over a type that knows how to parse record
492    /// data via the [`ParseRecordData`] trait. The record data is given to
493    /// this trait for parsing. If the trait feels capable of parsing this
494    /// type of record (as indicated by the record type) and parsing succeeds,
495    /// the method returns `Ok(Some(_))`. It returns `Ok(None)` if the trait
496    /// doesn’t know how to parse this particular record type. It returns
497    /// an error if parsing fails.
498    ///
499    /// [`ParseRecordData`]: ../rdata/trait.ParseRecordData.html
500    #[allow(type_complexity)] // I know ...
501    pub fn into_record<D>(
502        mut self
503    ) -> Result<Option<Record<ParsedDname, D>>,
504                RecordParseError<ParsedDnameError, D::Err>>
505    where D: ParseRecordData
506    {
507        match D::parse_data(self.header.rtype(), &mut self.data,
508                            self.header.rdlen() as usize)
509                .map_err(RecordParseError::Data)? {
510            Some(data) => Ok(Some(self.header.into_record(data))),
511            None => Ok(None)
512        }
513    }
514}
515
516
517//--- Parse
518//
519//    No Compose or Compress because the data may contain compressed domain
520//    names.
521
522impl Parse for ParsedRecord {
523    type Err = ParsedDnameError;
524
525    fn parse(parser: &mut Parser) -> Result<Self, Self::Err> {
526        let header = RecordHeader::parse(parser)?;
527        let data = parser.clone();
528        parser.advance(header.rdlen() as usize)?;
529        Ok(Self::new(header, data))
530    }
531
532    fn skip(parser: &mut Parser) -> Result<(), Self::Err> {
533        let rdlen = RecordHeader::parse_rdlen(parser)?;
534        parser.advance(rdlen as usize)?;
535        Ok(())
536    }
537}
538
539
540//------------ RecordParseError ----------------------------------------------
541
542#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
543pub enum RecordParseError<N: Fail, D: Fail> {
544    #[fail(display="{}", _0)]
545    Name(N),
546
547    #[fail(display="{}", _0)]
548    Data(D),
549
550    #[fail(display="unexpected end of buffer")]
551    ShortBuf,
552}
553
554impl<N: Fail, D: Fail> From<ShortBuf> for RecordParseError<N, D> {
555    fn from(_: ShortBuf) -> Self {
556        RecordParseError::ShortBuf
557    }
558}
559