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