Add new HTTPS and SVCB record types

This commit is contained in:
Benjamin Fry 2021-03-06 18:40:36 -08:00
parent 5f68b7d7b2
commit 276da8f67d
6 changed files with 551 additions and 34 deletions

View File

@ -1,4 +1,4 @@
// Copyright 2015-2020 Benjamin Fry <benjaminfry@me.com>
// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or

View File

@ -46,6 +46,9 @@ impl RDataParser for RData {
RecordType::CAA => caa::parse(tokens).map(RData::CAA)?,
RecordType::CNAME => RData::CNAME(name::parse(tokens, origin)?),
RecordType::HINFO => RData::HINFO(hinfo::parse(tokens)?),
// FIXME: actually implement this
#[allow(clippy::unimplemented)]
RecordType::HTTPS => unimplemented!("HTTPS records not yet supported in zone files"),
RecordType::IXFR => panic!("parsing IXFR doesn't make sense"), // valid panic, never should happen
RecordType::MX => RData::MX(mx::parse(tokens, origin)?),
RecordType::NAPTR => RData::NAPTR(naptr::parse(tokens, origin)?),
@ -57,6 +60,9 @@ impl RDataParser for RData {
RecordType::SOA => RData::SOA(soa::parse(tokens, origin)?),
RecordType::SRV => RData::SRV(srv::parse(tokens, origin)?),
RecordType::SSHFP => RData::SSHFP(sshfp::parse(tokens)?),
// FIXME: actually implement this
#[allow(clippy::unimplemented)]
RecordType::SVCB => unimplemented!("SVCB records not yet supported in zone files"),
RecordType::TLSA => RData::TLSA(tlsa::parse(tokens)?),
RecordType::TXT => RData::TXT(txt::parse(tokens)?),
RecordType::DNSSEC(DNSSECRecordType::SIG) => panic!("parsing SIG doesn't make sense"), // valid panic, never should happen

View File

@ -32,6 +32,7 @@ pub mod opt;
pub mod soa;
pub mod srv;
pub mod sshfp;
pub mod svcb;
pub mod tlsa;
pub mod txt;
@ -45,5 +46,6 @@ pub use self::opt::OPT;
pub use self::soa::SOA;
pub use self::srv::SRV;
pub use self::sshfp::SSHFP;
pub use self::svcb::SVCB;
pub use self::tlsa::TLSA;
pub use self::txt::TXT;

View File

@ -0,0 +1,448 @@
// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! SSHFP records for SSH public key fingerprints
use std::cmp::{Ord, Ordering, PartialOrd};
use std::fmt;
use crate::error::*;
use crate::rr::Name;
use crate::serialize::binary::*;
/// [draft-ietf-dnsop-svcb-https-03 SVCB and HTTPS RRs for DNS, February 2021](https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-03#section-2.2)
///
/// ```text
/// 2.2. RDATA wire format
///
/// The RDATA for the SVCB RR consists of:
///
/// * a 2 octet field for SvcPriority as an integer in network byte
/// order.
/// * the uncompressed, fully-qualified TargetName, represented as a
/// sequence of length-prefixed labels as in Section 3.1 of [RFC1035].
/// * the SvcParams, consuming the remainder of the record (so smaller
/// than 65535 octets and constrained by the RDATA and DNS message
/// sizes).
///
/// When the list of SvcParams is non-empty (ServiceMode), it contains a
/// series of SvcParamKey=SvcParamValue pairs, represented as:
///
/// * a 2 octet field containing the SvcParamKey as an integer in
/// network byte order. (See Section 14.3.2 for the defined values.)
/// * a 2 octet field containing the length of the SvcParamValue as an
/// integer between 0 and 65535 in network byte order (but constrained
/// by the RDATA and DNS message sizes).
/// * an octet string of this length whose contents are in a format
/// determined by the SvcParamKey.
///
/// SvcParamKeys SHALL appear in increasing numeric order.
///
/// Clients MUST consider an RR malformed if:
///
/// * the end of the RDATA occurs within a SvcParam.
/// * SvcParamKeys are not in strictly increasing numeric order.
/// * the SvcParamValue for an SvcParamKey does not have the expected
/// format.
///
/// Note that the second condition implies that there are no duplicate
/// SvcParamKeys.
///
/// If any RRs are malformed, the client MUST reject the entire RRSet and
/// fall back to non-SVCB connection establishment.
/// ```
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct SVCB {
svc_priority: u16,
target_name: Name,
svc_params: Vec<(SvcParamKey, SvcParamValue)>,
}
impl SVCB {
/// Create a new SVCB record from parts
///
/// It is up to the caller to validate the data going into the record
pub fn new(
svc_priority: u16,
target_name: Name,
svc_params: Vec<(SvcParamKey, SvcParamValue)>,
) -> Self {
Self {
svc_priority,
target_name,
svc_params,
}
}
}
/// ```text
/// 14.3.2. Initial contents
///
/// The "Service Binding (SVCB) Parameter Registry" shall initially be
/// populated with the registrations below:
///
/// +=============+=================+======================+===========+
/// | Number | Name | Meaning | Reference |
/// +=============+=================+======================+===========+
/// | 0 | mandatory | Mandatory keys in | (This |
/// | | | this RR | document) |
/// +-------------+-----------------+----------------------+-----------+
/// | 1 | alpn | Additional supported | (This |
/// | | | protocols | document) |
/// +-------------+-----------------+----------------------+-----------+
/// | 2 | no-default-alpn | No support for | (This |
/// | | | default protocol | document) |
/// +-------------+-----------------+----------------------+-----------+
/// | 3 | port | Port for alternative | (This |
/// | | | endpoint | document) |
/// +-------------+-----------------+----------------------+-----------+
/// | 4 | ipv4hint | IPv4 address hints | (This |
/// | | | | document) |
/// +-------------+-----------------+----------------------+-----------+
/// | 5 | echconfig | Encrypted | (This |
/// | | | ClientHello info | document) |
/// +-------------+-----------------+----------------------+-----------+
/// | 6 | ipv6hint | IPv6 address hints | (This |
/// | | | | document) |
/// +-------------+-----------------+----------------------+-----------+
/// | 65280-65534 | keyNNNNN | Private Use | (This |
/// | | | | document) |
/// +-------------+-----------------+----------------------+-----------+
/// | 65535 | key65535 | Reserved ("Invalid | (This |
/// | | | key") | document) |
/// +-------------+-----------------+----------------------+-----------+
///
/// parsing done via:
/// * a 2 octet field containing the SvcParamKey as an integer in
/// network byte order. (See Section 14.3.2 for the defined values.)
/// ```
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum SvcParamKey {
/// Mandatory keys in this RR
Mandatory,
/// Additional supported protocols
Alpn,
/// No support for default protocol
NoDefaultAlpn,
/// Port for alternative endpoint
Port,
/// IPv4 address hints
Ipv4Hint,
/// Encrypted ClientHello info
EchConfig,
/// IPv6 address hints
Ipv6Hint,
/// Private Use
Key(u16),
/// Reserved ("Invalid key")
Key65535,
/// Unknown
Unknown(u16),
}
impl From<u16> for SvcParamKey {
fn from(val: u16) -> Self {
match val {
0 => SvcParamKey::Mandatory,
1 => SvcParamKey::Alpn,
2 => SvcParamKey::NoDefaultAlpn,
3 => SvcParamKey::Port,
4 => SvcParamKey::Ipv4Hint,
5 => SvcParamKey::EchConfig,
6 => SvcParamKey::Ipv6Hint,
65280..=65534 => SvcParamKey::Key(val),
65535 => SvcParamKey::Key65535,
_ => SvcParamKey::Unknown(val),
}
}
}
impl From<SvcParamKey> for u16 {
fn from(val: SvcParamKey) -> Self {
match val {
SvcParamKey::Mandatory => 0,
SvcParamKey::Alpn => 1,
SvcParamKey::NoDefaultAlpn => 2,
SvcParamKey::Port => 3,
SvcParamKey::Ipv4Hint => 4,
SvcParamKey::EchConfig => 5,
SvcParamKey::Ipv6Hint => 6,
SvcParamKey::Key(val) => val,
SvcParamKey::Key65535 => 65535,
SvcParamKey::Unknown(val) => val,
}
}
}
impl fmt::Display for SvcParamKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let mut write_key = |name| write!(f, "{}", name);
match *self {
SvcParamKey::Mandatory => write_key("mandatory")?,
SvcParamKey::Alpn => write_key("alpn")?,
SvcParamKey::NoDefaultAlpn => write_key("no-default-alpn")?,
SvcParamKey::Port => write_key("port")?,
SvcParamKey::Ipv4Hint => write_key("ipv4hint")?,
SvcParamKey::EchConfig => write_key("echconfig")?,
SvcParamKey::Ipv6Hint => write_key("ipv6hint")?,
SvcParamKey::Key(val) => write!(f, "key{}", val)?,
SvcParamKey::Key65535 => write_key("key65535")?,
SvcParamKey::Unknown(val) => write!(f, "unknown{}", val)?,
}
Ok(())
}
}
impl Ord for SvcParamKey {
fn cmp(&self, other: &Self) -> Ordering {
u16::from(*self).cmp(&u16::from(*other))
}
}
impl PartialOrd for SvcParamKey {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
/// Warning, it is currently up to users of this type to validate the data against that expected by the key
///
/// ```text
/// * a 2 octet field containing the length of the SvcParamValue as an
/// integer between 0 and 65535 in network byte order (but constrained
/// by the RDATA and DNS message sizes).
/// * an octet string of this length whose contents are in a format
/// determined by the SvcParamKey.
/// ```
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[repr(transparent)]
pub struct SvcParamValue(Vec<u8>);
impl SvcParamValue {
/// Return the inner data as a slice of bytes
pub fn as_slice(&self) -> &[u8] {
&self.0
}
}
impl From<Vec<u8>> for SvcParamValue {
fn from(val: Vec<u8>) -> Self {
Self(val)
}
}
impl From<SvcParamValue> for Vec<u8> {
fn from(val: SvcParamValue) -> Self {
val.0
}
}
/// Reads the SVCB record from the decoder.
///
/// ```text
/// Clients MUST consider an RR malformed if:
///
/// * the end of the RDATA occurs within a SvcParam.
/// * SvcParamKeys are not in strictly increasing numeric order.
/// * the SvcParamValue for an SvcParamKey does not have the expected
/// format.
///
/// Note that the second condition implies that there are no duplicate
/// SvcParamKeys.
///
/// If any RRs are malformed, the client MUST reject the entire RRSet and
/// fall back to non-SVCB connection establishment.
/// ```
pub fn read(decoder: &mut BinDecoder<'_>, rdata_length: Restrict<u16>) -> ProtoResult<SVCB> {
let start_index = decoder.index();
let svc_priority = decoder.read_u16()?.unverified(/*any u16 is valid*/);
let target_name = Name::read(decoder)?;
let mut remainder_len = rdata_length.map(|len| len as usize - (decoder.index() - start_index)).unverified(/*valid len*/);
let mut svc_params: Vec<(SvcParamKey, SvcParamValue)> = Vec::new();
// must have at least 4 bytes left for the key and the length
while remainder_len >= 4 {
// a 2 octet field containing the SvcParamKey as an integer in
// network byte order. (See Section 14.3.2 for the defined values.)
let key: SvcParamKey = decoder.read_u16()?.unverified(/*any u16 is valid*/).into();
// a 2 octet field containing the length of the SvcParamValue as an
// integer between 0 and 65535 in network byte order (but constrained
// by the RDATA and DNS message sizes).
let len: u16 = decoder
.read_u16()?
.verify_unwrap(|len| *len as usize <= remainder_len)
.map_err(|u| {
ProtoError::from(format!(
"length of SvcParamValue ({}) exceeds remainder in RDATA ({})",
u, remainder_len
))
})?;
// an octet string of this length whose contents are in a format
// determined by the SvcParamKey.
let value = decoder.read_vec(len as usize)?.unverified(/*char data for users*/);
if let Some(last_key) = svc_params.last().map(|(key, _)| key) {
if last_key >= &key {
return Err(ProtoError::from("SvcParams out of order"));
}
}
svc_params.push((key, value.into()));
remainder_len = rdata_length.map(|len| len as usize - (decoder.index() - start_index)).unverified(/*valid len*/);
}
Ok(SVCB {
svc_priority,
target_name,
svc_params,
})
}
/// Write the RData from the given Decoder
pub fn emit(encoder: &mut BinEncoder<'_>, svcb: &SVCB) -> ProtoResult<()> {
svcb.svc_priority.emit(encoder)?;
svcb.target_name.emit(encoder)?;
let mut last_key: Option<SvcParamKey> = None;
for (key, param) in svcb.svc_params.iter() {
if let Some(last_key) = last_key {
if key <= &last_key {
return Err(ProtoError::from("SvcParams out of order"));
}
}
if param.as_slice().len() > u16::MAX as usize {
return Err(ProtoError::from("SvcParams exceeds u16 max size"));
}
encoder.emit_u16((*key).into())?;
encoder.emit_u16(param.as_slice().len() as u16)?;
encoder.emit_vec(param.as_slice())?;
last_key = Some(*key);
}
Ok(())
}
/// [draft-ietf-dnsop-svcb-https-03 SVCB and HTTPS RRs for DNS, February 2021](https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-03#section-10.3)
///
/// ```text
/// simple.example. 7200 IN HTTPS 1 . alpn=h3
/// pool 7200 IN HTTPS 1 h3pool alpn=h2,h3 echconfig="123..."
/// HTTPS 2 . alpn=h2 echconfig="abc..."
/// @ 7200 IN HTTPS 0 www
/// _8765._baz.api.example.com. 7200 IN SVCB 0 svc4-baz.example.net.
/// ```
impl fmt::Display for SVCB {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"{svc_priority} {target_name}",
svc_priority = self.svc_priority,
target_name = self.target_name,
)?;
for (key, param) in self.svc_params.iter() {
let param = String::from_utf8_lossy(param.as_slice());
write!(f, " {key}=\"{param}\"", key = key, param = param)?
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_svcb_key() {
assert_eq!(SvcParamKey::Mandatory, 0.into());
assert_eq!(SvcParamKey::Alpn, 1.into());
assert_eq!(SvcParamKey::NoDefaultAlpn, 2.into());
assert_eq!(SvcParamKey::Port, 3.into());
assert_eq!(SvcParamKey::Ipv4Hint, 4.into());
assert_eq!(SvcParamKey::EchConfig, 5.into());
assert_eq!(SvcParamKey::Ipv6Hint, 6.into());
assert_eq!(SvcParamKey::Key(65280), 65280.into());
assert_eq!(SvcParamKey::Key(65534), 65534.into());
assert_eq!(SvcParamKey::Key65535, 65535.into());
assert_eq!(SvcParamKey::Unknown(65279), 65279.into());
}
#[test]
fn read_svcb_key_to_u16() {
assert_eq!(u16::from(SvcParamKey::Mandatory), 0);
assert_eq!(u16::from(SvcParamKey::Alpn), 1);
assert_eq!(u16::from(SvcParamKey::NoDefaultAlpn), 2);
assert_eq!(u16::from(SvcParamKey::Port), 3);
assert_eq!(u16::from(SvcParamKey::Ipv4Hint), 4);
assert_eq!(u16::from(SvcParamKey::EchConfig), 5);
assert_eq!(u16::from(SvcParamKey::Ipv6Hint), 6);
assert_eq!(u16::from(SvcParamKey::Key(65280)), 65280);
assert_eq!(u16::from(SvcParamKey::Key(65534)), 65534);
assert_eq!(u16::from(SvcParamKey::Key65535), 65535);
assert_eq!(u16::from(SvcParamKey::Unknown(65279)), 65279);
}
#[track_caller]
fn test_encode_decode(rdata: SVCB) {
let mut bytes = Vec::new();
let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
emit(&mut encoder, &rdata).expect("failed to emit SVCB");
let bytes = encoder.into_bytes();
println!("svcb: {}", rdata);
println!("bytes: {:?}", bytes);
let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
let read_rdata =
read(&mut decoder, Restrict::new(bytes.len() as u16)).expect("failed to read back");
assert_eq!(rdata, read_rdata);
}
#[test]
fn test_encode_decode_svcb() {
test_encode_decode(SVCB::new(
0,
Name::from_utf8("www.example.com.").unwrap(),
vec![],
));
test_encode_decode(SVCB::new(
0,
Name::from_utf8(".").unwrap(),
vec![(SvcParamKey::Alpn, "h2".as_bytes().to_vec().into())],
));
test_encode_decode(SVCB::new(
0,
Name::from_utf8("example.com.").unwrap(),
vec![
(SvcParamKey::Mandatory, "alpn".as_bytes().to_vec().into()),
(SvcParamKey::Alpn, "h2".as_bytes().to_vec().into()),
],
));
}
#[test]
#[should_panic]
fn test_encode_decode_svcb_bad_order() {
test_encode_decode(SVCB::new(
0,
Name::from_utf8(".").unwrap(),
vec![
(SvcParamKey::Alpn, "h2".as_bytes().to_vec().into()),
(SvcParamKey::Mandatory, "alpn".as_bytes().to_vec().into()),
],
));
}
}

View File

@ -1,18 +1,9 @@
/*
* 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.
*/
// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! record data enum variants
@ -27,7 +18,9 @@ use log::{trace, warn};
use super::domain::Name;
use super::rdata;
use super::rdata::{CAA, HINFO, MX, NAPTR, NULL, OPENPGPKEY, OPT, SOA, SRV, SSHFP, TLSA, TXT};
use super::rdata::{
CAA, HINFO, MX, NAPTR, NULL, OPENPGPKEY, OPT, SOA, SRV, SSHFP, SVCB, TLSA, TXT,
};
use super::record_type::RecordType;
use crate::error::*;
use crate::serialize::binary::*;
@ -195,6 +188,29 @@ pub enum RData {
/// `HINFO` is also used by [RFC 8482](https://tools.ietf.org/html/rfc8482)
HINFO(HINFO),
/// [RFC draft-ietf-dnsop-svcb-https-03, DNS SVCB and HTTPS RRs](https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-03#section-8)
///
/// ```text
/// 8. Using SVCB with HTTPS and HTTP
///
/// Use of any protocol with SVCB requires a protocol-specific mapping
/// specification. This section specifies the mapping for HTTPS and
/// HTTP.
///
/// To enable special handling for the HTTPS and HTTP use-cases, the
/// HTTPS RR type is defined as a SVCB-compatible RR type, specific to
/// the https and http schemes. Clients MUST NOT perform SVCB queries or
/// accept SVCB responses for "https" or "http" schemes.
///
/// The HTTPS RR wire format and presentation format are identical to
/// SVCB, and both share the SvcParamKey registry. SVCB semantics apply
/// equally to HTTPS RRs unless specified otherwise. The presentation
/// format of the record is:
///
/// Name TTL IN HTTPS SvcPriority TargetName SvcParams
/// ```
HTTPS(SVCB),
/// ```text
/// 3.3.9. MX RDATA format
///
@ -574,6 +590,31 @@ pub enum RData {
/// [RFC 7479](https://tools.ietf.org/html/rfc7479).
SSHFP(SSHFP),
/// [RFC draft-ietf-dnsop-svcb-https-03, DNS SVCB and HTTPS RRs](https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-03#section-2)
///
/// ```text
/// 2. The SVCB record type
///
/// The SVCB DNS resource record (RR) type (RR type 64) is used to locate
/// alternative endpoints for a service.
///
/// The algorithm for resolving SVCB records and associated address
/// records is specified in Section 3.
///
/// Other SVCB-compatible resource record types can also be defined as-
/// needed. In particular, the HTTPS RR (RR type 65) provides special
/// handling for the case of "https" origins as described in Section 8.
///
/// SVCB RRs are extensible by a list of SvcParams, which are pairs
/// consisting of a SvcParamKey and a SvcParamValue. Each SvcParamKey
/// has a presentation name and a registered number. Values are in a
/// format specific to the SvcParamKey. Their definition should specify
/// both their presentation format and wire encoding (e.g., domain names,
/// binary data, or numeric values). The initial SvcParamKeys and
/// formats are defined in Section 6.
/// ```
SVCB(SVCB),
/// [RFC 6698, DNS-Based Authentication for TLS](https://tools.ietf.org/html/rfc6698#section-2.1)
///
/// ```text
@ -672,6 +713,10 @@ impl RData {
trace!("reading HINFO");
rdata::hinfo::read(decoder).map(RData::HINFO)
}
RecordType::HTTPS => {
trace!("reading HTTPS");
rdata::svcb::read(decoder, rdata_length).map(RData::HTTPS)
}
RecordType::ZERO => {
trace!("reading EMPTY");
return Ok(RData::ZERO);
@ -716,6 +761,10 @@ impl RData {
trace!("reading SSHFP");
rdata::sshfp::read(decoder, rdata_length).map(RData::SSHFP)
}
RecordType::SVCB => {
trace!("reading SVCB");
rdata::svcb::read(decoder, rdata_length).map(RData::SVCB)
}
RecordType::TLSA => {
trace!("reading TLSA");
rdata::tlsa::read(decoder, rdata_length).map(RData::TLSA)
@ -831,6 +880,7 @@ impl RData {
rdata::name::emit(encoder, name)
}
RData::HINFO(ref hinfo) => rdata::hinfo::emit(encoder, hinfo),
RData::HTTPS(ref svcb) => rdata::svcb::emit(encoder, svcb),
RData::ZERO => Ok(()),
// to_lowercase for rfc4034 and rfc6840
RData::MX(ref mx) => rdata::mx::emit(encoder, mx),
@ -851,6 +901,7 @@ impl RData {
RData::SSHFP(ref sshfp) => {
encoder.with_canonical_names(|encoder| rdata::sshfp::emit(encoder, sshfp))
}
RData::SVCB(ref svcb) => rdata::svcb::emit(encoder, svcb),
RData::TLSA(ref tlsa) => {
encoder.with_canonical_names(|encoder| rdata::tlsa::emit(encoder, tlsa))
}
@ -870,6 +921,7 @@ impl RData {
RData::CAA(..) => RecordType::CAA,
RData::CNAME(..) => RecordType::CNAME,
RData::HINFO(..) => RecordType::HINFO,
RData::HTTPS(..) => RecordType::HTTPS,
RData::MX(..) => RecordType::MX,
RData::NAPTR(..) => RecordType::NAPTR,
RData::NS(..) => RecordType::NS,
@ -880,6 +932,7 @@ impl RData {
RData::SOA(..) => RecordType::SOA,
RData::SRV(..) => RecordType::SRV,
RData::SSHFP(..) => RecordType::SSHFP,
RData::SVCB(..) => RecordType::SVCB,
RData::TLSA(..) => RecordType::TLSA,
RData::TXT(..) => RecordType::TXT,
#[cfg(feature = "dnssec")]
@ -913,6 +966,7 @@ impl fmt::Display for RData {
// to_lowercase for rfc4034 and rfc6840
RData::CNAME(ref name) | RData::NS(ref name) | RData::PTR(ref name) => w(f, name),
RData::HINFO(ref hinfo) => w(f, hinfo),
RData::HTTPS(ref svcb) => w(f, svcb),
RData::ZERO => Ok(()),
// to_lowercase for rfc4034 and rfc6840
RData::MX(ref mx) => w(f, mx),
@ -926,6 +980,7 @@ impl fmt::Display for RData {
// to_lowercase for rfc4034 and rfc6840
RData::SRV(ref srv) => w(f, srv),
RData::SSHFP(ref sshfp) => w(f, sshfp),
RData::SVCB(ref svcb) => w(f, svcb),
RData::TLSA(ref tlsa) => w(f, tlsa),
RData::TXT(ref txt) => w(f, txt),
#[cfg(feature = "dnssec")]
@ -1158,6 +1213,7 @@ mod tests {
RData::CAA(..) => RecordType::CAA,
RData::CNAME(..) => RecordType::CNAME,
RData::HINFO(..) => RecordType::HINFO,
RData::HTTPS(..) => RecordType::HTTPS,
RData::MX(..) => RecordType::MX,
RData::NAPTR(..) => RecordType::NAPTR,
RData::NS(..) => RecordType::NS,
@ -1168,6 +1224,7 @@ mod tests {
RData::SOA(..) => RecordType::SOA,
RData::SRV(..) => RecordType::SRV,
RData::SSHFP(..) => RecordType::SSHFP,
RData::SVCB(..) => RecordType::SVCB,
RData::TLSA(..) => RecordType::TLSA,
RData::TXT(..) => RecordType::TXT,
#[cfg(feature = "dnssec")]

View File

@ -1,18 +1,9 @@
/*
* 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.
*/
// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! record type definitions
@ -64,6 +55,8 @@ pub enum RecordType {
/// RFC 1035[1] host information
HINFO,
// HIP, // 55 RFC 5205 Host Identity Protocol
/// RFC draft-ietf-dnsop-svcb-https-03 DNS SVCB and HTTPS RRs
HTTPS,
// IPSECKEY, // 45 RFC 4025 IPsec Key
/// RFC 1996 Incremental Zone Transfer
IXFR,
@ -90,6 +83,8 @@ pub enum RecordType {
SRV,
/// RFC 4255 SSH Public Key Fingerprint
SSHFP,
/// RFC draft-ietf-dnsop-svcb-https-03 DNS SVCB and HTTPS RRs
SVCB,
// TA, // 32768 N/A DNSSEC Trust Authorities
// TKEY, // 249 RFC 2930 Secret key record
/// RFC 6698 TLSA certificate association
@ -172,6 +167,7 @@ impl FromStr for RecordType {
"CAA" => Ok(RecordType::CAA),
"CNAME" => Ok(RecordType::CNAME),
"HINFO" => Ok(RecordType::HINFO),
"HTTPS" => Ok(RecordType::HTTPS),
"NULL" => Ok(RecordType::NULL),
"MX" => Ok(RecordType::MX),
"NAPTR" => Ok(RecordType::NAPTR),
@ -181,6 +177,7 @@ impl FromStr for RecordType {
"SOA" => Ok(RecordType::SOA),
"SRV" => Ok(RecordType::SRV),
"SSHFP" => Ok(RecordType::SSHFP),
"SVCB" => Ok(RecordType::SVCB),
"TLSA" => Ok(RecordType::TLSA),
"TXT" => Ok(RecordType::TXT),
"ANY" | "*" => Ok(RecordType::ANY),
@ -213,7 +210,8 @@ impl From<u16> for RecordType {
252 => RecordType::AXFR,
257 => RecordType::CAA,
5 => RecordType::CNAME,
0 => RecordType::ZERO,
13 => RecordType::HINFO,
65 => RecordType::HTTPS,
15 => RecordType::MX,
35 => RecordType::NAPTR,
2 => RecordType::NS,
@ -224,8 +222,10 @@ impl From<u16> for RecordType {
6 => RecordType::SOA,
33 => RecordType::SRV,
44 => RecordType::SSHFP,
64 => RecordType::SVCB,
52 => RecordType::TLSA,
16 => RecordType::TXT,
0 => RecordType::ZERO,
#[cfg(feature = "dnssec")]
48/*DNSKEY*/ |
43/*DS*/ |
@ -283,7 +283,8 @@ impl From<RecordType> for &'static str {
RecordType::CAA => "CAA",
RecordType::CNAME => "CNAME",
RecordType::HINFO => "HINFO",
RecordType::ZERO => "",
RecordType::HTTPS => "HTTPS",
RecordType::ZERO => "ZERO",
RecordType::IXFR => "IXFR",
RecordType::MX => "MX",
RecordType::NAPTR => "NAPTR",
@ -295,6 +296,7 @@ impl From<RecordType> for &'static str {
RecordType::SOA => "SOA",
RecordType::SRV => "SRV",
RecordType::SSHFP => "SSHFP",
RecordType::SVCB => "SVCB",
RecordType::TLSA => "TLSA",
RecordType::TXT => "TXT",
#[cfg(feature = "dnssec")]
@ -325,6 +327,7 @@ impl From<RecordType> for u16 {
RecordType::CAA => 257,
RecordType::CNAME => 5,
RecordType::HINFO => 13,
RecordType::HTTPS => 65,
RecordType::ZERO => 0,
RecordType::IXFR => 251,
RecordType::MX => 15,
@ -337,6 +340,7 @@ impl From<RecordType> for u16 {
RecordType::SOA => 6,
RecordType::SRV => 33,
RecordType::SSHFP => 44,
RecordType::SVCB => 64,
RecordType::TLSA => 52,
RecordType::TXT => 16,
#[cfg(feature = "dnssec")]