Add new HTTPS and SVCB record types
This commit is contained in:
parent
5f68b7d7b2
commit
276da8f67d
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
448
crates/proto/src/rr/rdata/svcb.rs
Normal file
448
crates/proto/src/rr/rdata/svcb.rs
Normal 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()),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
@ -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")]
|
||||
|
@ -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")]
|
||||
|
Loading…
Reference in New Issue
Block a user