#![allow(clippy::use_self)]
use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::{
cmp::{Ord, Ordering, PartialOrd},
convert::TryFrom,
fmt,
str::FromStr,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
error::{ProtoError, ProtoResult},
rr::{
Name, RData, RecordData, RecordDataDecodable, RecordType,
rdata::{A, AAAA},
},
serialize::{
binary::{
BinDecodable, BinDecoder, BinEncodable, BinEncoder, DecodeError, RDataEncoding,
Restrict, RestrictedMath,
},
txt::{Lexer, ParseError, Token},
},
};
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[non_exhaustive]
pub struct SVCB {
pub svc_priority: u16,
pub target_name: Name,
pub svc_params: Vec<(SvcParamKey, SvcParamValue)>,
}
impl SVCB {
pub fn new(
svc_priority: u16,
target_name: Name,
svc_params: Vec<(SvcParamKey, SvcParamValue)>,
) -> Self {
Self {
svc_priority,
target_name,
svc_params,
}
}
pub(crate) fn from_tokens<'i, I: Iterator<Item = &'i str>>(
mut tokens: I,
) -> Result<Self, ParseError> {
let svc_priority: u16 = tokens
.next()
.ok_or_else(|| ParseError::MissingToken("SvcPriority".to_string()))
.and_then(|s| s.parse().map_err(Into::into))?;
let target_name: Name = tokens
.next()
.ok_or_else(|| ParseError::MissingToken("Target".to_string()))
.and_then(|s| Name::from_str(s).map_err(ParseError::from))?;
let mut svc_params = Vec::new();
for token in tokens {
let mut key_value = token.splitn(2, '=');
let key = key_value
.next()
.ok_or_else(|| ParseError::MissingToken("SVCB SvcbParams missing".to_string()))?;
let mut value = key_value.next();
if let Some(value) = value.as_mut() {
if value.starts_with('"') && value.ends_with('"') {
*value = &value[1..value.len() - 1];
}
}
svc_params.push(into_svc_param(key, value)?);
}
Ok(SVCB::new(svc_priority, target_name, svc_params))
}
}
fn into_svc_param(
key: &str,
value: Option<&str>,
) -> Result<(SvcParamKey, SvcParamValue), ParseError> {
let key = SvcParamKey::from_str(key)?;
let value = parse_value(key, value)?;
Ok((key, value))
}
fn parse_value(key: SvcParamKey, value: Option<&str>) -> Result<SvcParamValue, ParseError> {
match key {
SvcParamKey::Mandatory => parse_mandatory(value),
SvcParamKey::Alpn => parse_alpn(value),
SvcParamKey::NoDefaultAlpn => parse_no_default_alpn(value),
SvcParamKey::Port => parse_port(value),
SvcParamKey::Ipv4Hint => parse_ipv4_hint(value),
SvcParamKey::Ipv6Hint => parse_ipv6_hint(value),
SvcParamKey::EchConfigList => parse_ech_config(value),
SvcParamKey::Key(_) => parse_unknown(value),
SvcParamKey::Key65535 | SvcParamKey::Unknown(_) => Err(ParseError::Message(
"Bad Key type or unsupported, see generic key option, e.g. key1234",
)),
}
}
fn parse_char_data(value: &str) -> Result<String, ParseError> {
let mut lex = Lexer::new(value);
let ch_data = lex
.next_token()?
.ok_or(ParseError::Message("expected character data"))?;
match ch_data {
Token::CharData(data) => Ok(data),
_ => Err(ParseError::Message("expected character data")),
}
}
fn parse_mandatory(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
let value = value.ok_or(ParseError::Message("expected at least one Mandatory field"))?;
let mandatories = parse_list::<SvcParamKey>(value)?;
Ok(SvcParamValue::Mandatory(Mandatory(mandatories)))
}
fn parse_alpn(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
let value = value.ok_or(ParseError::Message("expected at least one ALPN code"))?;
let alpns = parse_list::<String>(value).expect("infallible");
Ok(SvcParamValue::Alpn(Alpn(alpns)))
}
fn parse_no_default_alpn(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
if value.is_some() {
return Err(ParseError::Message("no value expected for NoDefaultAlpn"));
}
Ok(SvcParamValue::NoDefaultAlpn)
}
fn parse_port(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
let value = value.ok_or(ParseError::Message("a port number for the port option"))?;
let value = parse_char_data(value)?;
let port = u16::from_str(&value)?;
Ok(SvcParamValue::Port(port))
}
fn parse_ipv4_hint(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
let value = value.ok_or(ParseError::Message("expected at least one ipv4 hint"))?;
let hints = parse_list::<A>(value)?;
Ok(SvcParamValue::Ipv4Hint(IpHint(hints)))
}
fn parse_ipv6_hint(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
let value = value.ok_or(ParseError::Message("expected at least one ipv6 hint"))?;
let hints = parse_list::<AAAA>(value)?;
Ok(SvcParamValue::Ipv6Hint(IpHint(hints)))
}
fn parse_ech_config(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
let value = value.ok_or(ParseError::Message(
"expected a base64 encoded string for EchConfig",
))?;
let value = parse_char_data(value)?;
let ech_config_bytes = data_encoding::BASE64.decode(value.as_bytes())?;
Ok(SvcParamValue::EchConfigList(EchConfigList(
ech_config_bytes,
)))
}
fn parse_unknown(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
let unknown: Vec<u8> = if let Some(value) = value {
value.as_bytes().to_vec()
} else {
Vec::new()
};
Ok(SvcParamValue::Unknown(Unknown(unknown)))
}
fn parse_list<T>(value: &str) -> Result<Vec<T>, ParseError>
where
T: FromStr,
T::Err: Into<ParseError>,
{
let mut result = Vec::new();
let mut current_value = String::new();
let mut escaping = false;
for c in value.chars() {
match (c, escaping) {
(',', false) => {
result.push(T::from_str(&parse_char_data(¤t_value)?).map_err(Into::into)?);
current_value.clear()
}
('\\', false) => escaping = true,
(',', true) => {
current_value.push(',');
escaping = false
}
(_, true) => {
current_value.push(c);
escaping = false
}
(_, false) => current_value.push(c),
}
}
if !current_value.is_empty() {
result.push(T::from_str(&parse_char_data(¤t_value)?).map_err(Into::into)?);
}
Ok(result)
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum SvcParamKey {
#[cfg_attr(feature = "serde", serde(rename = "mandatory"))]
Mandatory,
#[cfg_attr(feature = "serde", serde(rename = "alpn"))]
Alpn,
#[cfg_attr(feature = "serde", serde(rename = "no-default-alpn"))]
NoDefaultAlpn,
#[cfg_attr(feature = "serde", serde(rename = "port"))]
Port,
#[cfg_attr(feature = "serde", serde(rename = "ipv4hint"))]
Ipv4Hint,
#[cfg_attr(feature = "serde", serde(rename = "ech"))]
EchConfigList,
#[cfg_attr(feature = "serde", serde(rename = "ipv6hint"))]
Ipv6Hint,
Key(u16),
Key65535,
Unknown(u16),
}
impl From<u16> for SvcParamKey {
fn from(val: u16) -> Self {
match val {
0 => Self::Mandatory,
1 => Self::Alpn,
2 => Self::NoDefaultAlpn,
3 => Self::Port,
4 => Self::Ipv4Hint,
5 => Self::EchConfigList,
6 => Self::Ipv6Hint,
65280..=65534 => Self::Key(val),
65535 => Self::Key65535,
_ => Self::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::EchConfigList => 5,
SvcParamKey::Ipv6Hint => 6,
SvcParamKey::Key(val) => val,
SvcParamKey::Key65535 => 65535,
SvcParamKey::Unknown(val) => val,
}
}
}
impl<'r> BinDecodable<'r> for SvcParamKey {
fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
Ok(decoder.read_u16()?.unverified().into())
}
}
impl BinEncodable for SvcParamKey {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
encoder.emit_u16((*self).into())
}
}
impl fmt::Display for SvcParamKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
Self::Mandatory => f.write_str("mandatory")?,
Self::Alpn => f.write_str("alpn")?,
Self::NoDefaultAlpn => f.write_str("no-default-alpn")?,
Self::Port => f.write_str("port")?,
Self::Ipv4Hint => f.write_str("ipv4hint")?,
Self::EchConfigList => f.write_str("ech")?,
Self::Ipv6Hint => f.write_str("ipv6hint")?,
Self::Key(val) => write!(f, "key{val}")?,
Self::Key65535 => f.write_str("key65535")?,
Self::Unknown(val) => write!(f, "unknown{val}")?,
}
Ok(())
}
}
impl FromStr for SvcParamKey {
type Err = ProtoError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
fn parse_unknown_key(key: &str) -> Result<SvcParamKey, ProtoError> {
let key_value = key.strip_prefix("key").ok_or_else(|| {
ProtoError::Msg(format!("bad formatted key ({key}), expected key1234"))
})?;
Ok(SvcParamKey::Key(u16::from_str(key_value)?))
}
let key = match s {
"mandatory" => Self::Mandatory,
"alpn" => Self::Alpn,
"no-default-alpn" => Self::NoDefaultAlpn,
"port" => Self::Port,
"ipv4hint" => Self::Ipv4Hint,
"ech" => Self::EchConfigList,
"ipv6hint" => Self::Ipv6Hint,
"key65535" => Self::Key65535,
_ => parse_unknown_key(s)?,
};
Ok(key)
}
}
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))
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum SvcParamValue {
#[cfg_attr(feature = "serde", serde(rename = "mandatory"))]
Mandatory(Mandatory),
#[cfg_attr(feature = "serde", serde(rename = "alpn"))]
Alpn(Alpn),
#[cfg_attr(feature = "serde", serde(rename = "no-default-alpn"))]
NoDefaultAlpn,
#[cfg_attr(feature = "serde", serde(rename = "port"))]
Port(u16),
#[cfg_attr(feature = "serde", serde(rename = "ipv4hint"))]
Ipv4Hint(IpHint<A>),
#[cfg_attr(feature = "serde", serde(rename = "ech"))]
EchConfigList(EchConfigList),
#[cfg_attr(feature = "serde", serde(rename = "ipv6hint"))]
Ipv6Hint(IpHint<AAAA>),
Unknown(Unknown),
}
impl SvcParamValue {
fn read(key: SvcParamKey, decoder: &mut BinDecoder<'_>) -> Result<Self, DecodeError> {
let len: usize = decoder
.read_u16()?
.verify_unwrap(|len| *len as usize <= decoder.len())
.map(|len| len as usize)
.map_err(|u| DecodeError::IncorrectRDataLengthRead {
read: decoder.len(),
len: u as usize,
})?;
let param_data = decoder.read_slice(len)?.unverified();
let mut decoder = BinDecoder::new(param_data);
let value = match key {
SvcParamKey::Mandatory => Self::Mandatory(Mandatory::read(&mut decoder)?),
SvcParamKey::Alpn => Self::Alpn(Alpn::read(&mut decoder)?),
SvcParamKey::NoDefaultAlpn => {
if len > 0 {
return Err(DecodeError::IncorrectRDataLengthRead { read: len, len: 0 });
}
Self::NoDefaultAlpn
}
SvcParamKey::Port => {
let port = decoder.read_u16()?.unverified();
Self::Port(port)
}
SvcParamKey::Ipv4Hint => Self::Ipv4Hint(IpHint::<A>::read(&mut decoder)?),
SvcParamKey::EchConfigList => Self::EchConfigList(EchConfigList::read(&mut decoder)?),
SvcParamKey::Ipv6Hint => Self::Ipv6Hint(IpHint::<AAAA>::read(&mut decoder)?),
SvcParamKey::Key(_) | SvcParamKey::Key65535 | SvcParamKey::Unknown(_) => {
Self::Unknown(Unknown::read(&mut decoder)?)
}
};
Ok(value)
}
}
impl BinEncodable for SvcParamValue {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
let place = encoder.place::<u16>()?;
match self {
Self::Mandatory(mandatory) => mandatory.emit(encoder)?,
Self::Alpn(alpn) => alpn.emit(encoder)?,
Self::NoDefaultAlpn => (),
Self::Port(port) => encoder.emit_u16(*port)?,
Self::Ipv4Hint(ip_hint) => ip_hint.emit(encoder)?,
Self::EchConfigList(ech_config) => ech_config.emit(encoder)?,
Self::Ipv6Hint(ip_hint) => ip_hint.emit(encoder)?,
Self::Unknown(unknown) => unknown.emit(encoder)?,
}
let len = u16::try_from(encoder.len_since_place(&place))
.map_err(|_| ProtoError::from("Total length of SvcParamValue exceeds u16::MAX"))?;
place.replace(encoder, len)?;
Ok(())
}
}
impl fmt::Display for SvcParamValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
Self::Mandatory(mandatory) => write!(f, "{mandatory}")?,
Self::Alpn(alpn) => write!(f, "{alpn}")?,
Self::NoDefaultAlpn => (),
Self::Port(port) => write!(f, "{port}")?,
Self::Ipv4Hint(ip_hint) => write!(f, "{ip_hint}")?,
Self::EchConfigList(ech_config) => write!(f, "{ech_config}")?,
Self::Ipv6Hint(ip_hint) => write!(f, "{ip_hint}")?,
Self::Unknown(unknown) => write!(f, "{unknown}")?,
}
Ok(())
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[repr(transparent)]
pub struct Mandatory(pub Vec<SvcParamKey>);
impl<'r> BinDecodable<'r> for Mandatory {
fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
let mut keys = Vec::with_capacity(1);
while decoder.peek().is_some() {
keys.push(SvcParamKey::read(decoder)?);
}
if keys.is_empty() {
return Err(DecodeError::SvcParamMissingValue);
}
Ok(Self(keys))
}
}
impl BinEncodable for Mandatory {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
if self.0.is_empty() {
return Err(ProtoError::from("Alpn expects at least one value"));
}
for key in self.0.iter() {
key.emit(encoder)?
}
Ok(())
}
}
impl fmt::Display for Mandatory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
for key in self.0.iter() {
write!(f, "{key},")?;
}
Ok(())
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[repr(transparent)]
pub struct Alpn(pub Vec<String>);
impl<'r> BinDecodable<'r> for Alpn {
fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
let mut alpns = Vec::with_capacity(1);
while decoder.peek().is_some() {
let alpn = decoder.read_character_data()?.unverified();
let alpn = String::from_utf8(alpn.to_vec())?;
alpns.push(alpn);
}
if alpns.is_empty() {
return Err(DecodeError::SvcParamMissingValue);
}
Ok(Self(alpns))
}
}
impl BinEncodable for Alpn {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
if self.0.is_empty() {
return Err(ProtoError::from("Alpn expects at least one value"));
}
for alpn in self.0.iter() {
encoder.emit_character_data(alpn)?
}
Ok(())
}
}
impl fmt::Display for Alpn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
for alpn in self.0.iter() {
write!(f, "{alpn},")?;
}
Ok(())
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(PartialEq, Eq, Hash, Clone)]
#[repr(transparent)]
pub struct EchConfigList(pub Vec<u8>);
impl<'r> BinDecodable<'r> for EchConfigList {
fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
let data =
decoder.read_vec(decoder.len())?.unverified();
Ok(Self(data))
}
}
impl BinEncodable for EchConfigList {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
encoder.emit_vec(&self.0)?;
Ok(())
}
}
impl fmt::Display for EchConfigList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "\"{}\"", data_encoding::BASE64.encode(&self.0))
}
}
impl fmt::Debug for EchConfigList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"\"EchConfig ({})\"",
data_encoding::BASE64.encode(&self.0)
)
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[repr(transparent)]
pub struct IpHint<T>(pub Vec<T>);
impl<'r, T> BinDecodable<'r> for IpHint<T>
where
T: BinDecodable<'r>,
{
fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
let mut ips = Vec::new();
while decoder.peek().is_some() {
ips.push(T::read(decoder)?)
}
Ok(Self(ips))
}
}
impl<T> BinEncodable for IpHint<T>
where
T: BinEncodable,
{
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
for ip in self.0.iter() {
ip.emit(encoder)?;
}
Ok(())
}
}
impl<T> fmt::Display for IpHint<T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
for ip in self.0.iter() {
write!(f, "{ip},")?;
}
Ok(())
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[repr(transparent)]
pub struct Unknown(pub Vec<u8>);
impl<'r> BinDecodable<'r> for Unknown {
fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
let len = decoder.len();
let data = decoder.read_vec(len)?;
let unknowns = data.unverified().to_vec();
Ok(Self(unknowns))
}
}
impl BinEncodable for Unknown {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
encoder.emit_vec(&self.0)?;
Ok(())
}
}
impl fmt::Display for Unknown {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "\"{}\",", String::from_utf8_lossy(&self.0))?;
Ok(())
}
}
impl BinEncodable for SVCB {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
let mut encoder = encoder.with_rdata_behavior(RDataEncoding::Other);
self.svc_priority.emit(&mut encoder)?;
self.target_name.emit(&mut encoder)?;
let mut last_key: Option<SvcParamKey> = None;
for (key, param) in self.svc_params.iter() {
if let Some(last_key) = last_key {
if key <= &last_key {
return Err(ProtoError::from("SvcParams out of order"));
}
}
key.emit(&mut encoder)?;
param.emit(&mut encoder)?;
last_key = Some(*key);
}
Ok(())
}
}
impl RecordDataDecodable<'_> for SVCB {
fn read_data(
decoder: &mut BinDecoder<'_>,
rdata_length: Restrict<u16>,
) -> Result<Self, DecodeError> {
let start_index = decoder.index();
let svc_priority = decoder.read_u16()?.unverified();
let target_name = Name::read(decoder)?;
let mut remainder_len = rdata_length
.map(|len| len as usize)
.checked_sub(decoder.index() - start_index)
.map_err(|len| DecodeError::IncorrectRDataLengthRead {
read: decoder.index() - start_index,
len,
})?
.unverified(); let mut svc_params: Vec<(SvcParamKey, SvcParamValue)> = Vec::new();
while remainder_len >= 4 {
let key = SvcParamKey::read(decoder)?;
let value = SvcParamValue::read(key, decoder)?;
if let Some(last_key) = svc_params.last().map(|(key, _)| key) {
if last_key >= &key {
return Err(DecodeError::SvcParamsOutOfOrder);
}
}
svc_params.push((key, value));
remainder_len = rdata_length
.map(|len| len as usize)
.checked_sub(decoder.index() - start_index)
.map_err(|len| DecodeError::IncorrectRDataLengthRead {
read: decoder.index() - start_index,
len,
})?
.unverified(); }
Ok(Self {
svc_priority,
target_name,
svc_params,
})
}
}
impl RecordData for SVCB {
fn try_borrow(data: &RData) -> Option<&Self> {
match data {
RData::SVCB(data) => Some(data),
_ => None,
}
}
fn record_type(&self) -> RecordType {
RecordType::SVCB
}
fn into_rdata(self) -> RData {
RData::SVCB(self)
}
}
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() {
write!(f, " {key}={param}")?
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use alloc::{borrow::ToOwned, string::ToString};
use crate::{rr::rdata::HTTPS, serialize::txt::Parser};
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::EchConfigList, 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::EchConfigList), 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::new(&mut bytes);
rdata.emit(&mut encoder).expect("failed to emit SVCB");
let bytes = encoder.into_bytes();
let mut decoder = BinDecoder::new(bytes);
let read_rdata = SVCB::read_data(&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,
SvcParamValue::Alpn(Alpn(vec!["h2".to_string()])),
)],
));
test_encode_decode(SVCB::new(
0,
Name::from_utf8("example.com.").unwrap(),
vec![
(
SvcParamKey::Mandatory,
SvcParamValue::Mandatory(Mandatory(vec![SvcParamKey::Alpn])),
),
(
SvcParamKey::Alpn,
SvcParamValue::Alpn(Alpn(vec!["h2".to_string()])),
),
],
));
}
#[test]
#[should_panic]
fn test_encode_decode_svcb_bad_order() {
test_encode_decode(SVCB::new(
0,
Name::from_utf8(".").unwrap(),
vec![
(
SvcParamKey::Alpn,
SvcParamValue::Alpn(Alpn(vec!["h2".to_string()])),
),
(
SvcParamKey::Mandatory,
SvcParamValue::Mandatory(Mandatory(vec![SvcParamKey::Alpn])),
),
],
));
}
#[test]
fn test_no_panic() {
const BUF: &[u8] = &[
255, 121, 0, 0, 0, 0, 40, 255, 255, 160, 160, 0, 0, 0, 64, 0, 1, 255, 158, 0, 0, 0, 8,
0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0,
];
assert!(crate::op::Message::from_vec(BUF).is_err());
}
#[test]
fn test_unrestricted_output_size() {
let svcb = SVCB::new(
8224,
Name::from_utf8(".").unwrap(),
vec![(
SvcParamKey::Unknown(8224),
SvcParamValue::Unknown(Unknown(vec![32; 257])),
)],
);
let mut buf = Vec::new();
let mut encoder = BinEncoder::new(&mut buf);
svcb.emit(&mut encoder).unwrap();
}
#[test]
fn test_unknown_value_round_trip() {
let svcb = SVCB::new(
8224,
Name::from_utf8(".").unwrap(),
vec![(
SvcParamKey::Unknown(8224),
SvcParamValue::Unknown(Unknown(vec![32; 10])),
)],
);
let mut buf = Vec::new();
let mut encoder = BinEncoder::new(&mut buf);
svcb.emit(&mut encoder).unwrap();
let mut decoder = BinDecoder::new(&buf);
let decoded = SVCB::read_data(
&mut decoder,
Restrict::new(u16::try_from(buf.len()).unwrap()),
)
.unwrap();
assert_eq!(svcb, decoded);
}
fn parse_record<D: RecordData>(txt: &str) -> D {
let records = Parser::new(txt, None, Some(Name::root()))
.parse()
.expect("failed to parse record")
.1;
let record_set = records.into_iter().next().expect("no record found").1;
D::try_borrow(&record_set.into_iter().next().unwrap().data)
.expect("Not the correct record")
.clone()
}
#[test]
fn test_parsing() {
let svcb: HTTPS = parse_record(CF_HTTPS_RECORD);
assert_eq!(svcb.svc_priority, 1);
assert_eq!(svcb.target_name, Name::root());
let mut params = svcb.svc_params.iter();
let param = params.next().expect("not alpn");
assert_eq!(param.0, SvcParamKey::Alpn);
let SvcParamValue::Alpn(value) = ¶m.1 else {
panic!("expected alpn");
};
assert_eq!(value.0, &["http/1.1", "h2"]);
let param = params.next().expect("ipv4hint");
assert_eq!(SvcParamKey::Ipv4Hint, param.0);
let SvcParamValue::Ipv4Hint(value) = ¶m.1 else {
panic!("expected ipv4hint");
};
assert_eq!(
value.0,
&[A::new(162, 159, 137, 85), A::new(162, 159, 138, 85)]
);
let param = params.next().expect("echconfig");
assert_eq!(SvcParamKey::EchConfigList, param.0);
let SvcParamValue::EchConfigList(value) = ¶m.1 else {
panic!("expected echconfig");
};
assert_eq!(
value.0,
data_encoding::BASE64.decode(b"AEX+DQBBtgAgACBMmGJQR02doup+5VPMjYpe5HQQ/bpntFCxDa8LT2PLAgAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA=").unwrap()
);
let param = params.next().expect("ipv6hint");
assert_eq!(SvcParamKey::Ipv6Hint, param.0);
let SvcParamValue::Ipv6Hint(value) = ¶m.1 else {
panic!("expected ipv6hint");
};
assert_eq!(
value.0,
&[
AAAA::new(0x2606, 0x4700, 0x7, 0, 0, 0, 0xa29f, 0x8955),
AAAA::new(0x2606, 0x4700, 0x7, 0, 0, 0, 0xa29f, 0x8a5)
]
);
}
#[test]
fn test_parse_display() {
let svcb: SVCB = parse_record(CF_SVCB_RECORD);
let svcb_display = svcb.to_string();
let svcb_display = format!("crypto.cloudflare.com. 299 IN SVCB {svcb_display}");
let svcb_display = parse_record(&svcb_display);
assert_eq!(svcb, svcb_display);
}
#[test]
fn test_parsing_https() {
let records = [GOOGLE_HTTPS_RECORD, CF_HTTPS_RECORD];
for record in records.iter() {
let svcb: HTTPS = parse_record(record);
assert_eq!(svcb.svc_priority, 1);
assert_eq!(svcb.target_name, Name::root());
}
}
#[test]
fn test_rfc9460_vectors() {
#[derive(Debug)]
struct TestVector {
record: &'static str,
record_type: RecordType,
target_name: Name,
priority: u16,
params: Vec<(SvcParamKey, SvcParamValue)>,
}
#[derive(Debug)]
enum RecordType {
SVCB,
HTTPS,
}
let vectors: [TestVector; 9] = [
TestVector {
record: "example.com. 42 HTTPS 0 foo.example.com.",
record_type: RecordType::HTTPS,
target_name: Name::from_str("foo.example.com.").unwrap(),
priority: 0,
params: Vec::new(),
},
TestVector {
record: "example.com. 42 SVCB 1 .",
record_type: RecordType::SVCB,
target_name: Name::from_str(".").unwrap(),
priority: 1,
params: Vec::new(),
},
TestVector {
record: "example.com. 42 SVCB 16 foo.example.com. port=53",
record_type: RecordType::SVCB,
target_name: Name::from_str("foo.example.com.").unwrap(),
priority: 16,
params: vec![(SvcParamKey::Port, SvcParamValue::Port(53))],
},
TestVector {
record: "example.com. 42 SVCB 1 foo.example.com. key667=hello",
record_type: RecordType::SVCB,
target_name: Name::from_str("foo.example.com.").unwrap(),
priority: 1,
params: vec![(
SvcParamKey::Key(667),
SvcParamValue::Unknown(Unknown(b"hello".into())),
)],
},
TestVector {
record: r#"example.com. 42 SVCB 1 foo.example.com. key667="hello\210qoo""#,
record_type: RecordType::SVCB,
target_name: Name::from_str("foo.example.com.").unwrap(),
priority: 1,
params: vec![(
SvcParamKey::Key(667),
SvcParamValue::Unknown(Unknown(b"hello\\210qoo".into())),
)],
},
TestVector {
record: r#"example.com. 42 SVCB 1 foo.example.com. (ipv6hint="2001:db8::1,2001:db8::53:1")"#,
record_type: RecordType::SVCB,
target_name: Name::from_str("foo.example.com.").unwrap(),
priority: 1,
params: vec![(
SvcParamKey::Ipv6Hint,
SvcParamValue::Ipv6Hint(IpHint(vec![
AAAA::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
AAAA::new(0x2001, 0xdb8, 0, 0, 0, 0, 0x53, 1),
])),
)],
},
TestVector {
record: r#"example.com. 42 SVCB 1 example.com. (ipv6hint="2001:db8:122:344::192.0.2.33")"#,
record_type: RecordType::SVCB,
target_name: Name::from_str("example.com.").unwrap(),
priority: 1,
params: vec![(
SvcParamKey::Ipv6Hint,
SvcParamValue::Ipv6Hint(IpHint(vec![AAAA::new(
0x2001, 0xdb8, 0x122, 0x344, 0, 0, 0xc000, 0x221,
)])),
)],
},
TestVector {
record: r#"example.com. 42 SVCB 16 foo.example.org. (alpn=h2,h3-19 mandatory=ipv4hint,alpn ipv4hint=192.0.2.1)"#,
record_type: RecordType::SVCB,
target_name: Name::from_str("foo.example.org.").unwrap(),
priority: 16,
params: vec![
(
SvcParamKey::Alpn,
SvcParamValue::Alpn(Alpn(vec!["h2".to_owned(), "h3-19".to_owned()])),
),
(
SvcParamKey::Mandatory,
SvcParamValue::Mandatory(Mandatory(vec![
SvcParamKey::Ipv4Hint,
SvcParamKey::Alpn,
])),
),
(
SvcParamKey::Ipv4Hint,
SvcParamValue::Ipv4Hint(IpHint(vec![A::new(192, 0, 2, 1)])),
),
],
},
TestVector {
record: r#"example.com. 42 SVCB 16 foo.example.org. alpn="f\\\\oo\,bar,h2""#,
record_type: RecordType::SVCB,
target_name: Name::from_str("foo.example.org.").unwrap(),
priority: 16,
params: vec![(
SvcParamKey::Alpn,
SvcParamValue::Alpn(Alpn(vec![r#"f\\oo,bar"#.to_owned(), "h2".to_owned()])),
)],
},
];
for record in vectors {
let expected_scvb = SVCB::new(record.priority, record.target_name, record.params);
match record.record_type {
RecordType::SVCB => {
let parsed: SVCB = parse_record(record.record);
assert_eq!(parsed, expected_scvb);
}
RecordType::HTTPS => {
let parsed: HTTPS = parse_record(record.record);
assert_eq!(parsed, HTTPS(expected_scvb));
}
};
}
}
const CF_SVCB_RECORD: &str = "crypto.cloudflare.com. 1664 IN SVCB 1 . alpn=\"http/1.1,h2\" ipv4hint=162.159.137.85,162.159.138.85 ech=AEX+DQBBtgAgACBMmGJQR02doup+5VPMjYpe5HQQ/bpntFCxDa8LT2PLAgAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA= ipv6hint=2606:4700:7::a29f:8955,2606:4700:7::a29f:8a5";
const CF_HTTPS_RECORD: &str = "crypto.cloudflare.com. 1664 IN HTTPS 1 . alpn=\"http/1.1,h2\" ipv4hint=162.159.137.85,162.159.138.85 ech=AEX+DQBBtgAgACBMmGJQR02doup+5VPMjYpe5HQQ/bpntFCxDa8LT2PLAgAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA= ipv6hint=2606:4700:7::a29f:8955,2606:4700:7::a29f:8a5";
const GOOGLE_HTTPS_RECORD: &str = "google.com 21132 IN HTTPS 1 . alpn=\"h2,h3\"";
}