blob: 5b34c172ea085588036dd3d57db1f79d301385af [file] [log] [blame]
/*
* Copyright (C) 2015-2019 Benjamin Fry <benjaminfry@me.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! record data enum variants
use std::cmp::Ordering;
#[cfg(test)]
use std::convert::From;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use enum_as_inner::EnumAsInner;
use log::{debug, warn};
use super::domain::Name;
use super::rdata;
use super::rdata::{CAA, MX, NAPTR, NULL, OPENPGPKEY, OPT, SOA, SRV, SSHFP, TLSA, TXT};
use super::record_type::RecordType;
use crate::error::*;
use crate::serialize::binary::*;
#[cfg(feature = "dnssec")]
use super::dnssec::rdata::DNSSECRData;
/// Record data enum variants
///
/// [RFC 1035](https://tools.ietf.org/html/rfc1035), DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987
///
/// ```text
/// 3.3. Standard RRs
///
/// The following RR definitions are expected to occur, at least
/// potentially, in all classes. In particular, NS, SOA, CNAME, and PTR
/// will be used in all classes, and have the same format in all classes.
/// Because their RDATA format is known, all domain names in the RDATA
/// section of these RRs may be compressed.
///
/// <domain-name> is a domain name represented as a series of labels, and
/// terminated by a label with zero length. <character-string> is a single
/// length octet followed by that number of characters. <character-string>
/// is treated as binary information, and can be up to 256 characters in
/// length (including the length octet).
/// ```
#[derive(Debug, EnumAsInner, PartialEq, Clone, Eq)]
pub enum RData {
/// ```text
/// -- RFC 1035 -- Domain Implementation and Specification November 1987
///
/// 3.4. Internet specific RRs
///
/// 3.4.1. A RDATA format
///
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | ADDRESS |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// where:
///
/// ADDRESS A 32 bit Internet address.
///
/// Hosts that have multiple Internet addresses will have multiple A
/// records.
///
/// A records cause no additional section processing. The RDATA section of
/// an A line in a master file is an Internet address expressed as four
/// decimal numbers separated by dots without any embedded spaces (e.g.,
/// "10.2.0.52" or "192.0.5.6").
/// ```
A(Ipv4Addr),
/// ```text
/// -- RFC 1886 -- IPv6 DNS Extensions December 1995
///
/// 2.2 AAAA data format
///
/// A 128 bit IPv6 address is encoded in the data portion of an AAAA
/// resource record in network byte order (high-order byte first).
/// ```
AAAA(Ipv6Addr),
/// ```text
/// 2. The ANAME resource record
///
/// This document defines the "ANAME" DNS resource record type, with RR
/// TYPE value [TBD].
///
/// 2.1. Presentation and wire format
///
/// The ANAME presentation format is identical to that of CNAME
/// [RFC1033]:
///
/// owner ttl class ANAME target
/// ```
ANAME(Name),
/// ```text
/// -- RFC 6844 Certification Authority Authorization January 2013
///
/// 5.1. Syntax
///
/// A CAA RR contains a single property entry consisting of a tag-value
/// pair. Each tag represents a property of the CAA record. The value
/// of a CAA property is that specified in the corresponding value field.
///
/// A domain name MAY have multiple CAA RRs associated with it and a
/// given property MAY be specified more than once.
///
/// The CAA data field contains one property entry. A property entry
/// consists of the following data fields:
///
/// +0-1-2-3-4-5-6-7-|0-1-2-3-4-5-6-7-|
/// | Flags | Tag Length = n |
/// +----------------+----------------+...+---------------+
/// | Tag char 0 | Tag char 1 |...| Tag char n-1 |
/// +----------------+----------------+...+---------------+
/// +----------------+----------------+.....+----------------+
/// | Value byte 0 | Value byte 1 |.....| Value byte m-1 |
/// +----------------+----------------+.....+----------------+
/// Where n is the length specified in the Tag length field and m is the
/// remaining octets in the Value field (m = d - n - 2) where d is the
/// length of the RDATA section.
/// ```
CAA(CAA),
/// ```text
/// 3.3. Standard RRs
///
/// The following RR definitions are expected to occur, at least
/// potentially, in all classes. In particular, NS, SOA, CNAME, and PTR
/// will be used in all classes, and have the same format in all classes.
/// Because their RDATA format is known, all domain names in the RDATA
/// section of these RRs may be compressed.
///
/// <domain-name> is a domain name represented as a series of labels, and
/// terminated by a label with zero length. <character-string> is a single
/// length octet followed by that number of characters. <character-string>
/// is treated as binary information, and can be up to 256 characters in
/// length (including the length octet).
///
/// 3.3.1. CNAME RDATA format
///
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / CNAME /
/// / /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// where:
///
/// CNAME A <domain-name> which specifies the canonical or primary
/// name for the owner. The owner name is an alias.
///
/// CNAME RRs cause no additional section processing, but name servers may
/// choose to restart the query at the canonical name in certain cases. See
/// the description of name server logic in [RFC-1034] for details.
/// ```
CNAME(Name),
/// ```text
/// 3.3.9. MX RDATA format
///
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | PREFERENCE |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / EXCHANGE /
/// / /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// where:
///
/// PREFERENCE A 16 bit integer which specifies the preference given to
/// this RR among others at the same owner. Lower values
/// are preferred.
///
/// EXCHANGE A <domain-name> which specifies a host willing to act as
/// a mail exchange for the owner name.
///
/// MX records cause type A additional section processing for the host
/// specified by EXCHANGE. The use of MX RRs is explained in detail in
/// [RFC-974].
/// ```
MX(MX),
/// [RFC 3403 DDDS DNS Database, October 2002](https://tools.ietf.org/html/rfc3403#section-4)
///
/// ```text
/// 4.1 Packet Format
///
/// The packet format of the NAPTR RR is given below. The DNS type code
/// for NAPTR is 35.
///
/// The packet format for the NAPTR record is as follows
/// 1 1 1 1 1 1
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | ORDER |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | PREFERENCE |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / FLAGS /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / SERVICES /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / REGEXP /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / REPLACEMENT /
/// / /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// <character-string> and <domain-name> as used here are defined in RFC
/// 1035 [7].
///
/// ORDER
/// A 16-bit unsigned integer specifying the order in which the NAPTR
/// records MUST be processed in order to accurately represent the
/// ordered list of Rules. The ordering is from lowest to highest.
/// If two records have the same order value then they are considered
/// to be the same rule and should be selected based on the
/// combination of the Preference values and Services offered.
///
/// PREFERENCE
/// Although it is called "preference" in deference to DNS
/// terminology, this field is equivalent to the Priority value in the
/// DDDS Algorithm. It is a 16-bit unsigned integer that specifies
/// the order in which NAPTR records with equal Order values SHOULD be
/// processed, low numbers being processed before high numbers. This
/// is similar to the preference field in an MX record, and is used so
/// domain administrators can direct clients towards more capable
/// hosts or lighter weight protocols. A client MAY look at records
/// with higher preference values if it has a good reason to do so
/// such as not supporting some protocol or service very well.
///
/// The important difference between Order and Preference is that once
/// a match is found the client MUST NOT consider records with a
/// different Order but they MAY process records with the same Order
/// but different Preferences. The only exception to this is noted in
/// the second important Note in the DDDS algorithm specification
/// concerning allowing clients to use more complex Service
/// determination between steps 3 and 4 in the algorithm. Preference
/// is used to give communicate a higher quality of service to rules
/// that are considered the same from an authority standpoint but not
/// from a simple load balancing standpoint.
///
/// It is important to note that DNS contains several load balancing
/// mechanisms and if load balancing among otherwise equal services
/// should be needed then methods such as SRV records or multiple A
/// records should be utilized to accomplish load balancing.
///
/// FLAGS
/// A <character-string> containing flags to control aspects of the
/// rewriting and interpretation of the fields in the record. Flags
/// are single characters from the set A-Z and 0-9. The case of the
/// alphabetic characters is not significant. The field can be empty.
///
/// It is up to the Application specifying how it is using this
/// Database to define the Flags in this field. It must define which
/// ones are terminal and which ones are not.
///
/// SERVICES
/// A <character-string> that specifies the Service Parameters
/// applicable to this this delegation path. It is up to the
/// Application Specification to specify the values found in this
/// field.
///
/// REGEXP
/// A <character-string> containing a substitution expression that is
/// applied to the original string held by the client in order to
/// construct the next domain name to lookup. See the DDDS Algorithm
/// specification for the syntax of this field.
///
/// As stated in the DDDS algorithm, The regular expressions MUST NOT
/// be used in a cumulative fashion, that is, they should only be
/// applied to the original string held by the client, never to the
/// domain name produced by a previous NAPTR rewrite. The latter is
/// tempting in some applications but experience has shown such use to
/// be extremely fault sensitive, very error prone, and extremely
/// difficult to debug.
///
/// REPLACEMENT
/// A <domain-name> which is the next domain-name to query for
/// depending on the potential values found in the flags field. This
/// field is used when the regular expression is a simple replacement
/// operation. Any value in this field MUST be a fully qualified
/// domain-name. Name compression is not to be used for this field.
///
/// This field and the REGEXP field together make up the Substitution
/// Expression in the DDDS Algorithm. It is simply a historical
/// optimization specifically for DNS compression that this field
/// exists. The fields are also mutually exclusive. If a record is
/// returned that has values for both fields then it is considered to
/// be in error and SHOULD be either ignored or an error returned.
/// ```
NAPTR(NAPTR),
/// ```text
/// 3.3.10. NULL RDATA format (EXPERIMENTAL)
///
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / <anything> /
/// / /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// Anything at all may be in the RDATA field so long as it is 65535 octets
/// or less.
///
/// NULL records cause no additional section processing. NULL RRs are not
/// allowed in master files. NULLs are used as placeholders in some
/// experimental extensions of the DNS.
/// ```
NULL(NULL),
/// ```text
/// 3.3.11. NS RDATA format
///
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / NSDNAME /
/// / /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// where:
///
/// NSDNAME A <domain-name> which specifies a host which should be
/// authoritative for the specified class and domain.
///
/// NS records cause both the usual additional section processing to locate
/// a type A record, and, when used in a referral, a special search of the
/// zone in which they reside for glue information.
///
/// The NS RR states that the named host should be expected to have a zone
/// starting at owner name of the specified class. Note that the class may
/// not indicate the protocol family which should be used to communicate
/// with the host, although it is typically a strong hint. For example,
/// hosts which are name servers for either Internet (IN) or Hesiod (HS)
/// class information are normally queried using IN class protocols.
/// ```
NS(Name),
/// [RFC 7929](https://tools.ietf.org/html/rfc7929#section-2.1)
///
/// ```text
/// The RDATA portion of an OPENPGPKEY resource record contains a single
/// value consisting of a Transferable Public Key formatted as specified
/// in [RFC4880].
/// ```
OPENPGPKEY(OPENPGPKEY),
/// ```text
/// RFC 6891 EDNS(0) Extensions April 2013
/// 6.1.2. Wire Format
///
/// +------------+--------------+------------------------------+
/// | Field Name | Field Type | Description |
/// +------------+--------------+------------------------------+
/// | NAME | domain name | MUST be 0 (root domain) |
/// | TYPE | u_int16_t | OPT (41) |
/// | CLASS | u_int16_t | requestor's UDP payload size |
/// | TTL | u_int32_t | extended RCODE and flags |
/// | RDLEN | u_int16_t | length of all RDATA |
/// | RDATA | octet stream | {attribute,value} pairs |
/// +------------+--------------+------------------------------+
///
/// The variable part of an OPT RR may contain zero or more options in
/// the RDATA. Each option MUST be treated as a bit field. Each option
/// is encoded as:
///
/// +0 (MSB) +1 (LSB)
/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
/// 0: | OPTION-CODE |
/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
/// 2: | OPTION-LENGTH |
/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
/// 4: | |
/// / OPTION-DATA /
/// / /
/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
/// ```
OPT(OPT),
/// ```text
/// 3.3.12. PTR RDATA format
///
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / PTRDNAME /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// where:
///
/// PTRDNAME A <domain-name> which points to some location in the
/// domain name space.
///
/// PTR records cause no additional section processing. These RRs are used
/// in special domains to point to some other location in the domain space.
/// These records are simple data, and don't imply any special processing
/// similar to that performed by CNAME, which identifies aliases. See the
/// description of the IN-ADDR.ARPA domain for an example.
/// ```
PTR(Name),
/// ```text
/// 3.3.13. SOA RDATA format
///
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / MNAME /
/// / /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / RNAME /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | SERIAL |
/// | |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | REFRESH |
/// | |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | RETRY |
/// | |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | EXPIRE |
/// | |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | MINIMUM |
/// | |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// where:
///
/// MNAME The <domain-name> of the name server that was the
/// original or primary source of data for this zone.
///
/// RNAME A <domain-name> which specifies the mailbox of the
/// person responsible for this zone.
///
/// SERIAL The unsigned 32 bit version number of the original copy
/// of the zone. Zone transfers preserve this value. This
/// value wraps and should be compared using sequence space
/// arithmetic.
///
/// REFRESH A 32 bit time interval before the zone should be
/// refreshed.
///
/// RETRY A 32 bit time interval that should elapse before a
/// failed refresh should be retried.
///
/// EXPIRE A 32 bit time value that specifies the upper limit on
/// the time interval that can elapse before the zone is no
/// longer authoritative.
///
/// MINIMUM The unsigned 32 bit minimum TTL field that should be
/// exported with any RR from this zone.
///
/// SOA records cause no additional section processing.
///
/// All times are in units of seconds.
///
/// Most of these fields are pertinent only for name server maintenance
/// operations. However, MINIMUM is used in all query operations that
/// retrieve RRs from a zone. Whenever a RR is sent in a response to a
/// query, the TTL field is set to the maximum of the TTL field from the RR
/// and the MINIMUM field in the appropriate SOA. Thus MINIMUM is a lower
/// bound on the TTL field for all RRs in a zone. Note that this use of
/// MINIMUM should occur when the RRs are copied into the response and not
/// when the zone is loaded from a master file or via a zone transfer. The
/// reason for this provision is to allow future dynamic update facilities to
/// change the SOA RR with known semantics.
/// ```
SOA(SOA),
/// ```text
/// RFC 2782 DNS SRV RR February 2000
///
/// The format of the SRV RR
///
/// _Service._Proto.Name TTL Class SRV Priority Weight Port Target
/// ```
SRV(SRV),
/// [RFC 4255](https://tools.ietf.org/html/rfc4255#section-3.1)
///
/// ```text
/// 3.1. The SSHFP RDATA Format
///
/// The RDATA for a SSHFP RR consists of an algorithm number, fingerprint
/// type and the fingerprint of the public host key.
///
/// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// | algorithm | fp type | /
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ /
/// / /
/// / fingerprint /
/// / /
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
///
/// 3.1.1. Algorithm Number Specification
///
/// This algorithm number octet describes the algorithm of the public
/// key. The following values are assigned:
///
/// Value Algorithm name
/// ----- --------------
/// 0 reserved
/// 1 RSA
/// 2 DSS
///
/// Reserving other types requires IETF consensus [4].
///
/// 3.1.2. Fingerprint Type Specification
///
/// The fingerprint type octet describes the message-digest algorithm
/// used to calculate the fingerprint of the public key. The following
/// values are assigned:
///
/// Value Fingerprint type
/// ----- ----------------
/// 0 reserved
/// 1 SHA-1
///
/// Reserving other types requires IETF consensus [4].
///
/// For interoperability reasons, as few fingerprint types as possible
/// should be reserved. The only reason to reserve additional types is
/// to increase security.
///
/// 3.1.3. Fingerprint
///
/// The fingerprint is calculated over the public key blob as described
/// in [7].
///
/// The message-digest algorithm is presumed to produce an opaque octet
/// string output, which is placed as-is in the RDATA fingerprint field.
/// ```
///
/// The algorithm and fingerprint type values have been updated in
/// [RFC 6594](https://tools.ietf.org/html/rfc6594) and
/// [RFC 7479](https://tools.ietf.org/html/rfc7479).
SSHFP(SSHFP),
/// [RFC 6698, DNS-Based Authentication for TLS](https://tools.ietf.org/html/rfc6698#section-2.1)
///
/// ```text
/// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// | Cert. Usage | Selector | Matching Type | /
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ /
/// / /
/// / Certificate Association Data /
/// / /
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// ```
TLSA(TLSA),
/// ```text
/// 3.3.14. TXT RDATA format
///
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / TXT-DATA /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// where:
///
/// TXT-DATA One or more <character-string>s.
///
/// TXT RRs are used to hold descriptive text. The semantics of the text
/// depends on the domain where it is found.
/// ```
TXT(TXT),
/// A DNSSEC- or SIG(0)- specific record. See `DNSSECRData` for details.
///
/// These types are in `DNSSECRData` to make them easy to disable when
/// crypto functionality isn't needed.
#[cfg(feature = "dnssec")]
DNSSEC(DNSSECRData),
/// Unknown RecordData is for record types not supported by Trust-DNS
Unknown {
/// RecordType code
code: u16,
/// RData associated to the record
rdata: NULL,
},
/// This corresponds to a record type of 0, unspecified
ZERO,
}
impl RData {
fn to_bytes(&self) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
{
let mut encoder: BinEncoder = BinEncoder::new(&mut buf);
self.emit(&mut encoder).unwrap_or_else(|_| {
warn!("could not encode RDATA: {:?}", self);
});
}
buf
}
/// Read the RData from the given Decoder
pub fn read(
decoder: &mut BinDecoder,
record_type: RecordType,
rdata_length: Restrict<u16>,
) -> ProtoResult<Self> {
let start_idx = decoder.index();
let result = match record_type {
RecordType::A => {
debug!("reading A");
rdata::a::read(decoder).map(RData::A)
}
RecordType::AAAA => {
debug!("reading AAAA");
rdata::aaaa::read(decoder).map(RData::AAAA)
}
RecordType::ANAME => {
debug!("reading ANAME");
rdata::name::read(decoder).map(RData::ANAME)
}
rt @ RecordType::ANY | rt @ RecordType::AXFR | rt @ RecordType::IXFR => {
return Err(ProtoErrorKind::UnknownRecordTypeValue(rt.into()).into());
}
RecordType::CAA => {
debug!("reading CAA");
rdata::caa::read(decoder, rdata_length).map(RData::CAA)
}
RecordType::CNAME => {
debug!("reading CNAME");
rdata::name::read(decoder).map(RData::CNAME)
}
RecordType::ZERO => {
debug!("reading EMPTY");
return Ok(RData::ZERO);
}
RecordType::MX => {
debug!("reading MX");
rdata::mx::read(decoder).map(RData::MX)
}
RecordType::NAPTR => {
debug!("reading NAPTR");
rdata::naptr::read(decoder).map(RData::NAPTR)
}
RecordType::NULL => {
debug!("reading NULL");
rdata::null::read(decoder, rdata_length).map(RData::NULL)
}
RecordType::NS => {
debug!("reading NS");
rdata::name::read(decoder).map(RData::NS)
}
RecordType::OPENPGPKEY => {
debug!("reading OPENPGPKEY");
rdata::openpgpkey::read(decoder, rdata_length).map(RData::OPENPGPKEY)
}
RecordType::OPT => {
debug!("reading OPT");
rdata::opt::read(decoder, rdata_length).map(RData::OPT)
}
RecordType::PTR => {
debug!("reading PTR");
rdata::name::read(decoder).map(RData::PTR)
}
RecordType::SOA => {
debug!("reading SOA");
rdata::soa::read(decoder).map(RData::SOA)
}
RecordType::SRV => {
debug!("reading SRV");
rdata::srv::read(decoder).map(RData::SRV)
}
RecordType::SSHFP => {
debug!("reading SSHFP");
rdata::sshfp::read(decoder, rdata_length).map(RData::SSHFP)
}
RecordType::TLSA => {
debug!("reading TLSA");
rdata::tlsa::read(decoder, rdata_length).map(RData::TLSA)
}
RecordType::TXT => {
debug!("reading TXT");
rdata::txt::read(decoder, rdata_length).map(RData::TXT)
}
#[cfg(feature = "dnssec")]
RecordType::DNSSEC(record_type) => {
DNSSECRData::read(decoder, record_type, rdata_length).map(RData::DNSSEC)
}
RecordType::Unknown(code) => {
debug!("reading Unknown");
rdata::null::read(decoder, rdata_length).map(|rdata| RData::Unknown { code, rdata })
}
};
// we should have read rdata_length, but we did not
let read = decoder.index() - start_idx;
rdata_length
.map(|u| u as usize)
.verify_unwrap(|rdata_length| read == *rdata_length)
.map_err(|rdata_length| {
ProtoError::from(ProtoErrorKind::IncorrectRDataLengthRead {
read,
len: rdata_length,
})
})?;
result
}
/// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-6), DNSSEC Resource Records, March 2005
///
/// ```text
/// 6.2. Canonical RR Form
///
/// For the purposes of DNS security, the canonical form of an RR is the
/// wire format of the RR where:
///
/// ...
///
/// 3. if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
/// HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
/// SRV, DNAME, A6, RRSIG, or (rfc6840 removes NSEC), all uppercase
/// US-ASCII letters in the DNS names contained within the RDATA are replaced
/// by the corresponding lowercase US-ASCII letters;
/// ```
///
/// Canonical name form for all non-1035 records:
/// [RFC 3579](https://tools.ietf.org/html/rfc3597)
/// ```text
/// 4. Domain Name Compression
///
/// RRs containing compression pointers in the RDATA part cannot be
/// treated transparently, as the compression pointers are only
/// meaningful within the context of a DNS message. Transparently
/// copying the RDATA into a new DNS message would cause the compression
/// pointers to point at the corresponding location in the new message,
/// which now contains unrelated data. This would cause the compressed
/// name to be corrupted.
///
/// To avoid such corruption, servers MUST NOT compress domain names
/// embedded in the RDATA of types that are class-specific or not well-
/// known. This requirement was stated in [RFC1123] without defining the
/// term "well-known"; it is hereby specified that only the RR types
/// defined in [RFC1035] are to be considered "well-known".
///
/// The specifications of a few existing RR types have explicitly allowed
/// compression contrary to this specification: [RFC2163] specified that
/// compression applies to the PX RR, and [RFC2535] allowed compression
/// in SIG RRs and NXT RRs records. Since this specification disallows
/// compression in these cases, it is an update to [RFC2163] (section 4)
/// and [RFC2535] (sections 4.1.7 and 5.2).
///
/// Receiving servers MUST decompress domain names in RRs of well-known
/// type, and SHOULD also decompress RRs of type RP, AFSDB, RT, SIG, PX,
/// NXT, NAPTR, and SRV (although the current specification of the SRV RR
/// in [RFC2782] prohibits compression, [RFC2052] mandated it, and some
/// servers following that earlier specification are still in use).
///
/// Future specifications for new RR types that contain domain names
/// within their RDATA MUST NOT allow the use of name compression for
/// those names, and SHOULD explicitly state that the embedded domain
/// names MUST NOT be compressed.
///
/// As noted in [RFC1123], the owner name of an RR is always eligible for
/// compression.
///
/// ...
/// As a courtesy to implementors, it is hereby noted that the complete
/// set of such previously published RR types that contain embedded
/// domain names, and whose DNSSEC canonical form therefore involves
/// downcasing according to the DNS rules for character comparisons,
/// consists of the RR types NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
/// HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX, SRV,
/// DNAME, and A6.
/// ...
/// ```
pub fn emit(&self, encoder: &mut BinEncoder) -> ProtoResult<()> {
match *self {
RData::A(address) => rdata::a::emit(encoder, address),
RData::AAAA(ref address) => rdata::aaaa::emit(encoder, address),
RData::ANAME(ref name) => {
encoder.with_canonical_names(|encoder| rdata::name::emit(encoder, name))
}
RData::CAA(ref caa) => {
encoder.with_canonical_names(|encoder| rdata::caa::emit(encoder, caa))
}
// to_lowercase for rfc4034 and rfc6840
RData::CNAME(ref name) | RData::NS(ref name) | RData::PTR(ref name) => {
rdata::name::emit(encoder, name)
}
RData::ZERO => Ok(()),
// to_lowercase for rfc4034 and rfc6840
RData::MX(ref mx) => rdata::mx::emit(encoder, mx),
RData::NAPTR(ref naptr) => {
encoder.with_canonical_names(|encoder| rdata::naptr::emit(encoder, naptr))
}
RData::NULL(ref null) => rdata::null::emit(encoder, null),
RData::OPENPGPKEY(ref openpgpkey) => {
encoder.with_canonical_names(|encoder| rdata::openpgpkey::emit(encoder, openpgpkey))
}
RData::OPT(ref opt) => rdata::opt::emit(encoder, opt),
// to_lowercase for rfc4034 and rfc6840
RData::SOA(ref soa) => rdata::soa::emit(encoder, soa),
// to_lowercase for rfc4034 and rfc6840
RData::SRV(ref srv) => {
encoder.with_canonical_names(|encoder| rdata::srv::emit(encoder, srv))
}
RData::SSHFP(ref sshfp) => {
encoder.with_canonical_names(|encoder| rdata::sshfp::emit(encoder, sshfp))
}
RData::TLSA(ref tlsa) => {
encoder.with_canonical_names(|encoder| rdata::tlsa::emit(encoder, tlsa))
}
RData::TXT(ref txt) => rdata::txt::emit(encoder, txt),
#[cfg(feature = "dnssec")]
RData::DNSSEC(ref rdata) => encoder.with_canonical_names(|encoder| rdata.emit(encoder)),
RData::Unknown { ref rdata, .. } => rdata::null::emit(encoder, rdata),
}
}
/// Converts this to a Recordtype
pub fn to_record_type(&self) -> RecordType {
match *self {
RData::A(..) => RecordType::A,
RData::AAAA(..) => RecordType::AAAA,
RData::ANAME(..) => RecordType::ANAME,
RData::CAA(..) => RecordType::CAA,
RData::CNAME(..) => RecordType::CNAME,
RData::MX(..) => RecordType::MX,
RData::NAPTR(..) => RecordType::NAPTR,
RData::NS(..) => RecordType::NS,
RData::NULL(..) => RecordType::NULL,
RData::OPENPGPKEY(..) => RecordType::OPENPGPKEY,
RData::OPT(..) => RecordType::OPT,
RData::PTR(..) => RecordType::PTR,
RData::SOA(..) => RecordType::SOA,
RData::SRV(..) => RecordType::SRV,
RData::SSHFP(..) => RecordType::SSHFP,
RData::TLSA(..) => RecordType::TLSA,
RData::TXT(..) => RecordType::TXT,
#[cfg(feature = "dnssec")]
RData::DNSSEC(ref rdata) => RecordType::DNSSEC(DNSSECRData::to_record_type(rdata)),
RData::Unknown { code, .. } => RecordType::Unknown(code),
RData::ZERO => RecordType::ZERO,
}
}
/// If this is an A or AAAA record type, then an IpAddr will be returned
pub fn to_ip_addr(&self) -> Option<IpAddr> {
match *self {
RData::A(a) => Some(IpAddr::from(a)),
RData::AAAA(aaaa) => Some(IpAddr::from(aaaa)),
_ => None,
}
}
}
impl PartialOrd<RData> for RData {
fn partial_cmp(&self, other: &RData) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for RData {
// RFC 4034 DNSSEC Resource Records March 2005
//
// 6.3. Canonical RR Ordering within an RRset
//
// For the purposes of DNS security, RRs with the same owner name,
// class, and type are sorted by treating the RDATA portion of the
// canonical form of each RR as a left-justified unsigned octet sequence
// in which the absence of an octet sorts before a zero octet.
//
// [RFC2181] specifies that an RRset is not allowed to contain duplicate
// records (multiple RRs with the same owner name, class, type, and
// RDATA). Therefore, if an implementation detects duplicate RRs when
// putting the RRset in canonical form, it MUST treat this as a protocol
// error. If the implementation chooses to handle this protocol error
// in the spirit of the robustness principle (being liberal in what it
// accepts), it MUST remove all but one of the duplicate RR(s) for the
// purposes of calculating the canonical form of the RRset.
fn cmp(&self, other: &Self) -> Ordering {
// TODO: how about we just store the bytes with the decoded data?
// the decoded data is useful for queries, the encoded data is needed for transfers, signing
// and ordering.
self.to_bytes().cmp(&other.to_bytes())
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
use std::net::Ipv4Addr;
use std::net::Ipv6Addr;
use std::str::FromStr;
use super::*;
use crate::rr::domain::Name;
use crate::rr::rdata::{MX, SOA, SRV, TXT};
use crate::serialize::binary::bin_tests::test_emit_data_set;
#[allow(clippy::useless_attribute)]
#[allow(unused)]
use crate::serialize::binary::*;
fn get_data() -> Vec<(RData, Vec<u8>)> {
vec![
(
RData::CNAME(Name::from_str("www.example.com").unwrap()),
vec![
3, b'w', b'w', b'w', 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c',
b'o', b'm', 0,
],
),
(
RData::MX(MX::new(256, Name::from_str("n").unwrap())),
vec![1, 0, 1, b'n', 0],
),
(
RData::NS(Name::from_str("www.example.com").unwrap()),
vec![
3, b'w', b'w', b'w', 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c',
b'o', b'm', 0,
],
),
(
RData::PTR(Name::from_str("www.example.com").unwrap()),
vec![
3, b'w', b'w', b'w', 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c',
b'o', b'm', 0,
],
),
(
RData::SOA(SOA::new(
Name::from_str("www.example.com").unwrap(),
Name::from_str("xxx.example.com").unwrap(),
u32::max_value(),
-1 as i32,
-1 as i32,
-1 as i32,
u32::max_value(),
)),
vec![
3, b'w', b'w', b'w', 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c',
b'o', b'm', 0, 3, b'x', b'x', b'x', 0xC0, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF,
],
),
(
RData::TXT(TXT::new(vec![
"abcdef".to_string(),
"ghi".to_string(),
"".to_string(),
"j".to_string(),
])),
vec![
6, b'a', b'b', b'c', b'd', b'e', b'f', 3, b'g', b'h', b'i', 0, 1, b'j',
],
),
(
RData::A(Ipv4Addr::from_str("0.0.0.0").unwrap()),
vec![0, 0, 0, 0],
),
(
RData::AAAA(Ipv6Addr::from_str("::").unwrap()),
vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
),
(
RData::SRV(SRV::new(
1,
2,
3,
Name::from_str("www.example.com").unwrap(),
)),
vec![
0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 3, b'w', b'w', b'w', 7, b'e', b'x', b'a',
b'm', b'p', b'l', b'e', 3, b'c', b'o', b'm', 0,
],
),
]
}
// TODO this test kinda sucks, shows the problem with not storing the binary parts
#[test]
fn test_order() {
let ordered: Vec<RData> = vec![
RData::A(Ipv4Addr::from_str("0.0.0.0").unwrap()),
RData::AAAA(Ipv6Addr::from_str("::").unwrap()),
RData::SRV(SRV::new(
1,
2,
3,
Name::from_str("www.example.com").unwrap(),
)),
RData::MX(MX::new(256, Name::from_str("n").unwrap())),
RData::CNAME(Name::from_str("www.example.com").unwrap()),
RData::PTR(Name::from_str("www.example.com").unwrap()),
RData::NS(Name::from_str("www.example.com").unwrap()),
RData::SOA(SOA::new(
Name::from_str("www.example.com").unwrap(),
Name::from_str("xxx.example.com").unwrap(),
u32::max_value(),
-1 as i32,
-1 as i32,
-1 as i32,
u32::max_value(),
)),
RData::TXT(TXT::new(vec![
"abcdef".to_string(),
"ghi".to_string(),
"".to_string(),
"j".to_string(),
])),
];
let mut unordered = vec![
RData::CNAME(Name::from_str("www.example.com").unwrap()),
RData::MX(MX::new(256, Name::from_str("n").unwrap())),
RData::PTR(Name::from_str("www.example.com").unwrap()),
RData::NS(Name::from_str("www.example.com").unwrap()),
RData::SOA(SOA::new(
Name::from_str("www.example.com").unwrap(),
Name::from_str("xxx.example.com").unwrap(),
u32::max_value(),
-1 as i32,
-1 as i32,
-1 as i32,
u32::max_value(),
)),
RData::TXT(TXT::new(vec![
"abcdef".to_string(),
"ghi".to_string(),
"".to_string(),
"j".to_string(),
])),
RData::A(Ipv4Addr::from_str("0.0.0.0").unwrap()),
RData::AAAA(Ipv6Addr::from_str("::").unwrap()),
RData::SRV(SRV::new(
1,
2,
3,
Name::from_str("www.example.com").unwrap(),
)),
];
unordered.sort();
assert_eq!(ordered, unordered);
}
#[test]
fn test_read() {
for (test_pass, (expect, binary)) in get_data().into_iter().enumerate() {
println!("test {}: {:?}", test_pass, binary);
let length = binary.len() as u16; // pre exclusive borrow
let mut decoder = BinDecoder::new(&binary);
assert_eq!(
RData::read(
&mut decoder,
record_type_from_rdata(&expect),
Restrict::new(length)
)
.unwrap(),
expect
);
}
}
fn record_type_from_rdata(rdata: &RData) -> crate::rr::record_type::RecordType {
match *rdata {
RData::A(..) => RecordType::A,
RData::AAAA(..) => RecordType::AAAA,
RData::ANAME(..) => RecordType::ANAME,
RData::CAA(..) => RecordType::CAA,
RData::CNAME(..) => RecordType::CNAME,
RData::MX(..) => RecordType::MX,
RData::NAPTR(..) => RecordType::NAPTR,
RData::NS(..) => RecordType::NS,
RData::NULL(..) => RecordType::NULL,
RData::OPENPGPKEY(..) => RecordType::OPENPGPKEY,
RData::OPT(..) => RecordType::OPT,
RData::PTR(..) => RecordType::PTR,
RData::SOA(..) => RecordType::SOA,
RData::SRV(..) => RecordType::SRV,
RData::SSHFP(..) => RecordType::SSHFP,
RData::TLSA(..) => RecordType::TLSA,
RData::TXT(..) => RecordType::TXT,
#[cfg(feature = "dnssec")]
RData::DNSSEC(ref rdata) => RecordType::DNSSEC(rdata.to_record_type()),
RData::Unknown { code, .. } => RecordType::Unknown(code),
RData::ZERO => RecordType::ZERO,
}
}
#[test]
fn test_write_to() {
test_emit_data_set(get_data(), |e, d| d.emit(e));
}
}