#![allow(clippy::needless_maybe_sized)]
use crate::base::cmp::CanonicalOrd;
use crate::base::iana::{IpseckeyAlgorithm, IpseckeyGatewayType, Rtype};
use crate::base::name::FlattenInto;
use crate::base::rdata::{ComposeRecordData, RecordData};
use crate::base::scan::{Scan, Scanner, ScannerError};
use crate::base::wire::{Composer, FormError, ParseError};
use crate::base::zonefile_fmt::{self, Formatter, ZonefileFmt};
use crate::base::{ParsedName, ToName};
use crate::utils::base64;
use core::cmp::Ordering;
use core::{fmt, hash};
use octseq::octets::{Octets, OctetsFrom, OctetsInto};
use octseq::parse::Parser;
use super::{Aaaa, A};
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(
serialize = "
N: serde::Serialize,
Octs: octseq::serde::SerializeOctets
",
deserialize = "
N: serde::Deserialize<'de>,
Octs: octseq::serde::DeserializeOctets<'de>
",
))
)]
pub struct Ipseckey<Octs: ?Sized, N> {
precedence: u8,
gateway_type: IpseckeyGatewayType,
algorithm: IpseckeyAlgorithm,
gateway: IpseckeyGateway<N>,
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "octseq::serde::SerializeOctets::serialize_octets",
deserialize_with = "octseq::serde::DeserializeOctets::deserialize_octets",
)
)]
key: Octs,
}
impl Ipseckey<(), ()> {
pub(crate) const RTYPE: Rtype = Rtype::IPSECKEY;
}
impl<Octs, N> Ipseckey<Octs, N> {
pub fn new(
precedence: u8,
algorithm: IpseckeyAlgorithm,
gateway: IpseckeyGateway<N>,
key: Octs,
) -> Self {
let gateway_type = match gateway {
IpseckeyGateway::None => IpseckeyGatewayType::NONE,
IpseckeyGateway::Ipv4(_) => IpseckeyGatewayType::IPV4,
IpseckeyGateway::Ipv6(_) => IpseckeyGatewayType::IPV6,
IpseckeyGateway::Name(_) => IpseckeyGatewayType::NAME,
};
Self {
precedence,
gateway_type,
algorithm,
gateway,
key,
}
}
pub fn precedence(&self) -> u8 {
self.precedence
}
pub fn gateway_type(&self) -> IpseckeyGatewayType {
self.gateway_type
}
pub fn algorithm(&self) -> IpseckeyAlgorithm {
self.algorithm
}
pub fn gateway(&self) -> &IpseckeyGateway<N> {
&self.gateway
}
pub fn key(&self) -> &Octs {
&self.key
}
pub fn scan<S: Scanner<Octets = Octs, Name = N>>(
scanner: &mut S,
) -> Result<Self, S::Error>
where
Octs: AsRef<[u8]>,
{
let precedence = u8::scan(scanner)?;
let gateway_type = u8::scan(scanner)?.into();
let algorithm = u8::scan(scanner)?.into();
let gateway = IpseckeyGateway::scan(scanner, gateway_type)?;
let key = scanner.convert_entry(base64::SymbolConverter::new())?;
if key.as_ref().is_empty() && algorithm != IpseckeyAlgorithm::NONE {
return Err(ScannerError::custom("Missing IPSECKEY public key field. The public key field may only be omitted when the algorithm is specified as 0"));
}
Ok(Self {
precedence,
gateway_type,
algorithm,
gateway,
key,
})
}
pub(super) fn flatten<TargetOcts, TargetName>(
self,
) -> Result<Ipseckey<TargetOcts, TargetName>, N::AppendError>
where
TargetOcts: OctetsFrom<Octs>,
N: FlattenInto<TargetName, AppendError = TargetOcts::Error>,
{
let Ipseckey {
precedence,
gateway_type,
algorithm,
gateway,
key,
} = self;
Ok(Ipseckey {
precedence,
gateway_type,
algorithm,
gateway: gateway.flatten()?,
key: key.try_octets_into()?,
})
}
pub(super) fn convert_octets<TargetOcts, TargetName>(
self,
) -> Result<Ipseckey<TargetOcts, TargetName>, TargetOcts::Error>
where
TargetOcts: OctetsFrom<Octs>,
TargetName: OctetsFrom<N, Error = TargetOcts::Error>,
{
let Ipseckey {
precedence,
gateway_type,
algorithm,
gateway,
key,
} = self;
Ok(Ipseckey {
precedence,
gateway_type,
algorithm,
gateway: gateway.convert_octets()?,
key: key.try_octets_into()?,
})
}
}
impl<Octs> Ipseckey<Octs, ParsedName<Octs>> {
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
parser: &mut Parser<'a, Src>,
) -> Result<Self, ParseError> {
let precedence = parser.parse_u8()?;
let gateway_type = IpseckeyGatewayType::parse(parser)?;
let algorithm = IpseckeyAlgorithm::parse(parser)?;
let gateway = IpseckeyGateway::parse(parser, gateway_type)?;
let len_key = parser.remaining();
if len_key == 0 && algorithm != IpseckeyAlgorithm::NONE {
return Err(ParseError::ShortInput);
}
let key = parser.parse_octets(len_key)?;
Ok(Self {
precedence,
gateway_type,
algorithm,
gateway,
key,
})
}
}
impl<Octs, N> RecordData for Ipseckey<Octs, N> {
fn rtype(&self) -> Rtype {
Ipseckey::RTYPE
}
}
impl<Octs: AsRef<[u8]>, N: ToName> ComposeRecordData for Ipseckey<Octs, N> {
fn rdlen(&self, _compress: bool) -> Option<u16> {
Some(
u16::try_from(
1 + 1
+ 1
+ self.gateway.rdlen() as usize
+ self.key.as_ref().len(),
)
.expect("long IPSECKEY rdata"),
)
}
fn compose_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
target.append_slice(&[self.precedence])?;
target.append_slice(&[self.gateway_type.into()])?;
target.append_slice(&[self.algorithm.into()])?;
self.gateway.compose_rdata(target)?;
target.append_slice(self.key.as_ref())
}
fn compose_canonical_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
self.compose_rdata(target)
}
}
impl<Octs: AsRef<[u8]>, N: hash::Hash> hash::Hash for Ipseckey<Octs, N> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.precedence.hash(state);
self.gateway_type.hash(state);
self.algorithm.hash(state);
self.gateway.hash(state);
self.key.as_ref().hash(state);
}
}
impl<Octs, N, OtherOcts, OtherName> PartialEq<Ipseckey<OtherOcts, OtherName>>
for Ipseckey<Octs, N>
where
Octs: AsRef<[u8]> + ?Sized,
OtherOcts: AsRef<[u8]> + ?Sized,
N: ToName,
OtherName: ToName,
{
fn eq(&self, other: &Ipseckey<OtherOcts, OtherName>) -> bool {
self.precedence.eq(&other.precedence)
&& self.gateway_type.eq(&other.gateway_type)
&& self.algorithm.eq(&other.algorithm)
&& self.gateway.eq(&other.gateway)
&& self.key.as_ref().eq(other.key.as_ref())
}
}
impl<Octs: AsRef<[u8]> + ?Sized, N: ToName> Eq for Ipseckey<Octs, N> {}
impl<Octs: AsRef<[u8]>, N: fmt::Display> fmt::Display for Ipseckey<Octs, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {} {} {} ( ",
self.precedence,
u8::from(self.gateway_type),
u8::from(self.algorithm),
self.gateway,
)?;
base64::display(&self.key, f)?;
write!(f, " )")
}
}
impl<Octs: AsRef<[u8]>, N: fmt::Debug> fmt::Debug for Ipseckey<Octs, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Ipseckey")
.field("precedence", &self.precedence)
.field("gateway_type", &self.gateway_type)
.field("algorithm", &self.algorithm)
.field("gateway", &self.gateway)
.field(
"key",
&format_args!("{}", base64::encode_display(&self.key)),
)
.finish()
}
}
impl<Octs: AsRef<[u8]>, N: ToName> ZonefileFmt for Ipseckey<Octs, N> {
fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
p.block(|p| {
p.write_token(self.precedence)?;
p.write_comment("precedence")?;
p.write_show(self.gateway_type)?;
p.write_comment("gateway type")?;
p.write_show(self.algorithm)?;
p.write_comment("algorithm")?;
p.write_show(&self.gateway)?;
p.write_comment("gateway")?;
p.write_token(base64::encode_display(&self.key))
})
}
}
impl<Octs, OtherOcts, N, OtherName> PartialOrd<Ipseckey<OtherOcts, OtherName>>
for Ipseckey<Octs, N>
where
Octs: AsRef<[u8]>,
OtherOcts: AsRef<[u8]>,
N: ToName,
OtherName: ToName,
{
fn partial_cmp(
&self,
other: &Ipseckey<OtherOcts, OtherName>,
) -> Option<Ordering> {
match self.precedence.partial_cmp(&other.precedence) {
Some(Ordering::Equal) => {}
other => return other,
}
match self.gateway_type.partial_cmp(&other.gateway_type) {
Some(Ordering::Equal) => {}
other => return other,
}
match self.algorithm.partial_cmp(&other.algorithm) {
Some(Ordering::Equal) => {}
other => return other,
}
match self.gateway.partial_cmp(&other.gateway) {
Some(Ordering::Equal) => {}
other => return other,
}
self.key.as_ref().partial_cmp(other.key.as_ref())
}
}
impl<Octs, OtherOcts, N, OtherName>
CanonicalOrd<Ipseckey<OtherOcts, OtherName>> for Ipseckey<Octs, N>
where
Octs: AsRef<[u8]>,
OtherOcts: AsRef<[u8]>,
N: ToName,
OtherName: ToName,
{
fn canonical_cmp(
&self,
other: &Ipseckey<OtherOcts, OtherName>,
) -> Ordering {
match self.precedence.cmp(&other.precedence) {
Ordering::Equal => {}
other => return other,
}
match self.gateway_type.cmp(&other.gateway_type) {
Ordering::Equal => {}
other => return other,
}
match self.algorithm.cmp(&other.algorithm) {
Ordering::Equal => {}
other => return other,
}
match self.gateway.partial_cmp(&other.gateway) {
Some(Ordering::Equal) => {}
Some(other) => return other,
None => unreachable!("The gateway will be the same variant and therefore have an ordering, because the gateway_type above was Equal"),
}
self.key.as_ref().cmp(other.key.as_ref())
}
}
impl<Octs: AsRef<[u8]>, N: ToName> Ord for Ipseckey<Octs, N> {
fn cmp(&self, other: &Self) -> Ordering {
match self.precedence.cmp(&other.precedence) {
Ordering::Equal => {}
other => return other,
}
match self.gateway_type.cmp(&other.gateway_type) {
Ordering::Equal => {}
other => return other,
}
match self.algorithm.cmp(&other.algorithm) {
Ordering::Equal => {}
other => return other,
}
match self.gateway.partial_cmp(&other.gateway) {
Some(Ordering::Equal) => {}
Some(other) => return other,
None => unreachable!("The gateway will be the same variant and therefore have an ordering, because the gateway_type above was Equal"),
}
self.key.as_ref().cmp(other.key.as_ref())
}
}
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum IpseckeyGateway<N> {
None,
Ipv4(A),
Ipv6(Aaaa),
Name(N),
}
impl<N> IpseckeyGateway<N> {
pub fn rdlen(&self) -> u16
where
N: ToName,
{
match self {
IpseckeyGateway::None => 0,
IpseckeyGateway::Ipv4(_) => 4,
IpseckeyGateway::Ipv6(_) => 16,
IpseckeyGateway::Name(n) => n.compose_len(),
}
}
pub fn is_correct_gateway_type(&self, gwt: IpseckeyGatewayType) -> bool {
matches!(
(self, gwt),
(IpseckeyGateway::None, IpseckeyGatewayType::NONE)
| (IpseckeyGateway::Ipv4(_), IpseckeyGatewayType::IPV4)
| (IpseckeyGateway::Ipv6(_), IpseckeyGatewayType::IPV6)
| (IpseckeyGateway::Name(_), IpseckeyGatewayType::NAME)
)
}
pub fn scan<S: Scanner<Name = N>>(
scanner: &mut S,
gateway_type: IpseckeyGatewayType,
) -> Result<Self, S::Error> {
Ok(match gateway_type {
IpseckeyGatewayType::NONE => {
scanner.scan_ascii_str(|s| {
if s == "." {
Ok(Self::None)
} else {
Err(ScannerError::custom("Invalid IPSECKEY gateway. As the gateway type is specified as 0 (None), the gateway MUST be set to '.'"))
}
})?
},
IpseckeyGatewayType::IPV4 => Self::Ipv4(A::scan(scanner)?),
IpseckeyGatewayType::IPV6 => Self::Ipv6(Aaaa::scan(scanner)?),
IpseckeyGatewayType::NAME => Self::Name(scanner.scan_name()?),
_ => {
return Err(ScannerError::custom(
"Unknown IPSECKEY gateway type",
))
}
})
}
pub(super) fn flatten<TargetName>(
self,
) -> Result<IpseckeyGateway<TargetName>, N::AppendError>
where
N: FlattenInto<TargetName>,
{
Ok(match self {
IpseckeyGateway::None => IpseckeyGateway::None,
IpseckeyGateway::Ipv4(a) => IpseckeyGateway::Ipv4(a),
IpseckeyGateway::Ipv6(aaaa) => IpseckeyGateway::Ipv6(aaaa),
IpseckeyGateway::Name(n) => {
IpseckeyGateway::Name(n.try_flatten_into()?)
}
})
}
pub(super) fn convert_octets<Target: OctetsFrom<N>>(
self,
) -> Result<IpseckeyGateway<Target>, Target::Error> {
Ok(match self {
IpseckeyGateway::None => IpseckeyGateway::None,
IpseckeyGateway::Ipv4(a) => IpseckeyGateway::Ipv4(a),
IpseckeyGateway::Ipv6(aaaa) => IpseckeyGateway::Ipv6(aaaa),
IpseckeyGateway::Name(n) => {
IpseckeyGateway::Name(n.try_octets_into()?)
}
})
}
fn compose_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError>
where
N: ToName,
{
match self {
IpseckeyGateway::None => (),
IpseckeyGateway::Ipv4(a) => a.compose_rdata(target)?,
IpseckeyGateway::Ipv6(aaaa) => aaaa.compose_rdata(target)?,
IpseckeyGateway::Name(n) => n.compose(target)?,
};
Ok(())
}
}
impl<N: hash::Hash> hash::Hash for IpseckeyGateway<N> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
match self {
IpseckeyGateway::None => todo!(),
IpseckeyGateway::Ipv4(a) => a.hash(state),
IpseckeyGateway::Ipv6(aaaa) => aaaa.hash(state),
IpseckeyGateway::Name(n) => n.hash(state),
}
}
}
impl<N, OtherName> PartialEq<IpseckeyGateway<OtherName>>
for IpseckeyGateway<N>
where
N: ToName,
OtherName: ToName,
{
fn eq(&self, other: &IpseckeyGateway<OtherName>) -> bool {
match (self, other) {
(IpseckeyGateway::None, IpseckeyGateway::None) => true,
(IpseckeyGateway::Ipv4(a), IpseckeyGateway::Ipv4(o)) => a.eq(o),
(IpseckeyGateway::Ipv6(aaaa), IpseckeyGateway::Ipv6(o)) => {
aaaa.eq(o)
}
(IpseckeyGateway::Name(n), IpseckeyGateway::Name(o)) => {
n.name_eq(o)
}
_ => false,
}
}
}
impl<N, OtherName> PartialOrd<IpseckeyGateway<OtherName>>
for IpseckeyGateway<N>
where
N: ToName,
OtherName: ToName,
{
fn partial_cmp(
&self,
other: &IpseckeyGateway<OtherName>,
) -> Option<Ordering> {
match (self, other) {
(IpseckeyGateway::None, IpseckeyGateway::None) => {
Some(Ordering::Equal)
}
(IpseckeyGateway::Ipv4(a), IpseckeyGateway::Ipv4(o)) => {
a.partial_cmp(o)
}
(IpseckeyGateway::Ipv6(aaaa), IpseckeyGateway::Ipv6(o)) => {
aaaa.partial_cmp(o)
}
(IpseckeyGateway::Name(n), IpseckeyGateway::Name(o)) => {
Some(n.name_cmp(o))
}
_ => None,
}
}
}
impl<Octs> IpseckeyGateway<ParsedName<Octs>> {
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
parser: &mut Parser<'a, Src>,
gateway_type: IpseckeyGatewayType,
) -> Result<Self, ParseError> {
let len_gateway = match gateway_type {
IpseckeyGatewayType::NONE => Some(0),
IpseckeyGatewayType::IPV4 => Some(4),
IpseckeyGatewayType::IPV6 => Some(16),
IpseckeyGatewayType::NAME => None,
_ => {
return Err(ParseError::Form(FormError::new(
"Unknown IPSECKEY gateway type",
)))
}
};
let remaining = parser.remaining();
let gateway = if let Some(len_gateway) = len_gateway {
if remaining < len_gateway {
return Err(ParseError::ShortInput);
}
match gateway_type {
IpseckeyGatewayType::NONE => IpseckeyGateway::None,
IpseckeyGatewayType::IPV4 => {
IpseckeyGateway::Ipv4(A::parse(parser)?)
}
IpseckeyGatewayType::IPV6 => {
IpseckeyGateway::Ipv6(Aaaa::parse(parser)?)
}
_ => unreachable!(),
}
} else {
let name = ParsedName::parse(parser)?;
if name.is_compressed() {
return Err(ParseError::Form(FormError::new(
"IPSECKEY gateway contains compressed name",
)));
}
IpseckeyGateway::Name(name)
};
Ok(gateway)
}
}
impl<N: ToName> ZonefileFmt for IpseckeyGateway<N> {
fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
match self {
IpseckeyGateway::None => (),
IpseckeyGateway::Ipv4(a) => p.write_show(a)?,
IpseckeyGateway::Ipv6(aaaa) => p.write_show(aaaa)?,
IpseckeyGateway::Name(n) => p.write_token(n.fmt_with_dot())?,
};
Ok(())
}
}
impl<N: fmt::Display> fmt::Display for IpseckeyGateway<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IpseckeyGateway::None => write!(f, "."),
IpseckeyGateway::Ipv4(a) => write!(f, "{a}"),
IpseckeyGateway::Ipv6(aaaa) => write!(f, "{aaaa}"),
IpseckeyGateway::Name(n) => write!(f, "{n}"),
}
}
}
impl<N: fmt::Debug> fmt::Debug for IpseckeyGateway<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IpseckeyGateway::None => write!(f, "IpseckeyGateway::None"),
IpseckeyGateway::Ipv4(a) => {
write!(f, "IpseckeyGateway::Ipv4({a:?})")
}
IpseckeyGateway::Ipv6(aaaa) => {
write!(f, "IpseckeyGateway::Ipv6({aaaa:?})")
}
IpseckeyGateway::Name(n) => {
write!(f, "IpseckeyGateway::Name({n:?})")
}
}
}
}
#[cfg(test)]
#[cfg(all(feature = "std", feature = "bytes"))]
mod test {
use super::*;
use crate::base::rdata::test::{
test_compose_parse, test_rdlen, test_scan,
};
use crate::base::Name;
use crate::utils::base64::decode;
use core::str::FromStr;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::string::ToString;
use std::vec::Vec;
#[test]
#[allow(clippy::redundant_closure)]
fn ipseckey_compose_parse_scan() {
let key_str = "AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==";
let key: Vec<u8> = decode(key_str).unwrap();
for (precedence, gateway_type, algorithm, gateway_str, gateway) in [
(
10,
IpseckeyGatewayType::from_int(1),
2.into(),
"192.0.2.38",
IpseckeyGateway::<Name<Vec<u8>>>::Ipv4(
Ipv4Addr::new(192, 0, 2, 38).into(),
),
),
(
10,
0.into(),
2.into(),
".",
IpseckeyGateway::<Name<Vec<u8>>>::None,
),
(
10,
1.into(),
2.into(),
"192.0.2.3",
IpseckeyGateway::<Name<Vec<u8>>>::Ipv4(
Ipv4Addr::new(192, 0, 2, 3).into(),
),
),
(
10,
3.into(),
2.into(),
"mygateway.example.com.",
IpseckeyGateway::<Name<Vec<u8>>>::Name(
Name::from_str("mygateway.example.com.").unwrap(),
),
),
(
10,
2.into(),
2.into(),
"2001:0DB8:0:8002::2000:1",
IpseckeyGateway::<Name<Vec<u8>>>::Ipv6(
Ipv6Addr::new(
0x2001, 0x0DB8, 0x0, 0x8002, 0x0, 0x0, 0x2000, 0x1,
)
.into(),
),
),
] {
let rdata = Ipseckey::new(precedence, algorithm, gateway, &key);
test_rdlen(&rdata);
test_compose_parse(&rdata, |parser| Ipseckey::parse(parser));
test_scan(
&[
&precedence.to_string(),
&u8::from(gateway_type).to_string(),
&u8::from(algorithm).to_string(),
gateway_str,
key_str,
],
Ipseckey::scan,
&rdata,
);
}
let rdata = Ipseckey::new(
10,
0.into(),
IpseckeyGateway::<Name<Vec<u8>>>::None,
&[],
);
test_rdlen(&rdata);
test_compose_parse(&rdata, |parser| Ipseckey::parse(parser));
test_scan(
&[&10.to_string(), &0.to_string(), &0.to_string(), "."],
Ipseckey::scan,
&rdata,
);
}
#[test]
#[should_panic]
#[allow(clippy::redundant_closure)]
fn ipseckey_scan_wrong_gateway() {
let precedence = 10;
let gateway_type = IpseckeyGatewayType::from_int(0);
let algorithm = 2.into();
let wrong_gateway_str = "this.should.be.just.dot.";
let correct_gateway = IpseckeyGateway::<Name<Vec<u8>>>::None;
let key_str = "AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==";
let key: Vec<u8> = decode(key_str).unwrap();
let correct_rdata =
Ipseckey::new(precedence, algorithm, correct_gateway, key);
test_scan(
&[
&precedence.to_string(),
&u8::from(gateway_type).to_string(),
&u8::from(algorithm).to_string(),
wrong_gateway_str,
key_str,
],
Ipseckey::scan,
&correct_rdata,
);
}
#[cfg(feature = "zonefile")]
#[test]
fn ipseckey_parse_zonefile() {
use crate::rdata::ZoneRecordData;
use crate::zonefile::inplace::{Entry, Zonefile};
let content = r#"
arpa. 86400 IN SOA ns1 admin 2018031900 (
1800 900 604800 86400 )
86400 IN NS ns1
86400 IN NS ns2
ns1 3600 IN A 203.0.113.63
ns2 3600 IN AAAA 2001:db8::63
38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2 192.0.2.38
AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 0 2 .
AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2
192.0.2.3
AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
38.1.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 3 2
mygateway.example.com.
AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
$ORIGIN 1.0.0.0.0.0.2.8.B.D.0.1.0.0.2.ip6.arpa.
0.d.4.0.3.0.e.f.f.f.3.f.0.1.2.0 7200 IN IPSECKEY ( 10 2 2
2001:0DB8:0:8002::2000:1
AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
"#;
let mut zone = Zonefile::load(&mut content.as_bytes()).unwrap();
zone.set_origin(Name::root());
let key_str = "AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==";
let key: Vec<u8> = decode(key_str).unwrap();
let expected_ipseckeys = [
Ipseckey::new(
10,
2.into(),
IpseckeyGateway::<Name<Vec<u8>>>::Ipv4(
Ipv4Addr::new(192, 0, 2, 38).into(),
),
&key,
),
Ipseckey::new(
10,
2.into(),
IpseckeyGateway::<Name<Vec<u8>>>::None,
&key,
),
Ipseckey::new(
10,
2.into(),
IpseckeyGateway::<Name<Vec<u8>>>::Ipv4(
Ipv4Addr::new(192, 0, 2, 3).into(),
),
&key,
),
Ipseckey::new(
10,
2.into(),
IpseckeyGateway::<Name<Vec<u8>>>::Name(
Name::from_str("mygateway.example.com.").unwrap(),
),
&key,
),
Ipseckey::new(
10,
2.into(),
IpseckeyGateway::<Name<Vec<u8>>>::Ipv6(
Ipv6Addr::new(
0x2001, 0x0DB8, 0x0, 0x8002, 0x0, 0x0, 0x2000, 0x1,
)
.into(),
),
&key,
),
];
let mut expected_idx = 0;
while let Some(entry) = zone.next_entry().unwrap() {
match entry {
Entry::Record(record) => {
if record.rtype() != Rtype::IPSECKEY {
continue;
}
match record.into_data() {
ZoneRecordData::Ipseckey(rd) => {
assert_eq!(expected_ipseckeys[expected_idx], rd);
expected_idx += 1;
}
_ => panic!(),
}
}
_ => panic!(),
}
}
}
}