You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
700 lines
19 KiB
Rust
700 lines
19 KiB
Rust
use crate::common_types::*;
|
|
use crate::errors::*;
|
|
use crate::ser::packet::{get_blob, remaining_bytes, DnsPacketData, DnsPacketWriteContext};
|
|
use crate::ser::text::{next_field, DnsTextContext, DnsTextData, DnsTextFormatter};
|
|
use crate::ser::RRData;
|
|
use bytes::{Buf, BufMut, Bytes};
|
|
use failure::ResultExt;
|
|
use std::fmt;
|
|
use std::io::Read;
|
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
|
|
|
// deriving RRData will add a unit test to make sure the type is
|
|
// registered; there must be a records::types::$name `Type` constant
|
|
// with the same name as the struct.
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug, RRData)]
|
|
#[RRClass(ANY)]
|
|
pub enum LOC {
|
|
Version0(LOC0),
|
|
UnknownVersion { version: u8, data: Bytes },
|
|
}
|
|
|
|
impl DnsPacketData for LOC {
|
|
fn deserialize(data: &mut ::std::io::Cursor<Bytes>) -> Result<Self> {
|
|
let version: u8 = DnsPacketData::deserialize(data)?;
|
|
if 0 == version {
|
|
Ok(LOC::Version0(DnsPacketData::deserialize(data)?))
|
|
} else {
|
|
Ok(LOC::UnknownVersion {
|
|
version: version,
|
|
data: remaining_bytes(data),
|
|
})
|
|
}
|
|
}
|
|
|
|
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
|
match *self {
|
|
LOC::Version0(ref l0) => {
|
|
packet.reserve(1);
|
|
packet.put_u8(0);
|
|
l0.serialize(context, packet)
|
|
},
|
|
LOC::UnknownVersion { version, ref data } => {
|
|
packet.reserve(data.len() + 1);
|
|
packet.put_u8(version);
|
|
packet.put_slice(data);
|
|
Ok(())
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl DnsTextData for LOC {
|
|
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> Result<Self> {
|
|
let degrees_latitude = next_field(data)?.parse::<u8>()?;
|
|
failure::ensure!(
|
|
degrees_latitude <= 90,
|
|
"degrees latitude out of range: {}",
|
|
degrees_latitude
|
|
);
|
|
let mut minutes_latitude = 0;
|
|
let mut seconds_latitude = 0.0;
|
|
let mut field = next_field(data)?;
|
|
if field != "N" && field != "n" && field != "S" && field != "s" {
|
|
minutes_latitude = field.parse::<u8>()?;
|
|
failure::ensure!(
|
|
minutes_latitude < 60,
|
|
"minutes latitude out of range: {}",
|
|
minutes_latitude
|
|
);
|
|
field = next_field(data)?;
|
|
if field != "N" && field != "n" && field != "S" && field != "s" {
|
|
seconds_latitude = field.parse::<f32>()?;
|
|
failure::ensure!(
|
|
seconds_latitude >= 0.0 && seconds_latitude < 60.0,
|
|
"seconds latitude out of range: {}",
|
|
seconds_latitude
|
|
);
|
|
field = next_field(data)?;
|
|
}
|
|
}
|
|
let latitude_off = (3600_000 * degrees_latitude as u32)
|
|
+ (60_000 * minutes_latitude as u32)
|
|
+ (1_000.0 * seconds_latitude).round() as u32;
|
|
failure::ensure!(latitude_off <= 3600_000 * 180, "latitude out of range");
|
|
let latitude = match field {
|
|
"N" | "n" => 0x8000_0000 + latitude_off,
|
|
"S" | "s" => 0x8000_0000 - latitude_off,
|
|
_ => failure::bail!("invalid latitude orientation [NS]: {}", field),
|
|
};
|
|
|
|
let degrees_longitude = next_field(data)?.parse::<u8>()?;
|
|
failure::ensure!(
|
|
degrees_longitude <= 180,
|
|
"degrees longitude out of range: {}",
|
|
degrees_longitude
|
|
);
|
|
let mut minutes_longitude = 0;
|
|
let mut seconds_longitude = 0.0;
|
|
let mut field = next_field(data)?;
|
|
if field != "E" && field != "e" && field != "W" && field != "w" {
|
|
minutes_longitude = field.parse::<u8>()?;
|
|
failure::ensure!(
|
|
minutes_longitude < 60,
|
|
"minutes longitude out of range: {}",
|
|
minutes_longitude
|
|
);
|
|
field = next_field(data)?;
|
|
if field != "E" && field != "e" && field != "W" && field != "w" {
|
|
seconds_longitude = field.parse::<f32>()?;
|
|
failure::ensure!(
|
|
seconds_longitude >= 0.0 && seconds_longitude < 60.0,
|
|
"seconds longitude out of range: {}",
|
|
seconds_longitude
|
|
);
|
|
field = next_field(data)?;
|
|
}
|
|
}
|
|
let longitude_off = (3600_000 * degrees_longitude as u32)
|
|
+ (60_000 * minutes_longitude as u32)
|
|
+ (1_000.0 * seconds_longitude).round() as u32;
|
|
failure::ensure!(longitude_off <= 3600_000 * 180, "longitude out of range");
|
|
let longitude = match field {
|
|
"E" | "e" => 0x8000_0000 + longitude_off,
|
|
"W" | "w" => 0x8000_0000 - longitude_off,
|
|
_ => failure::bail!("invalid longitude orientation [EW]: {}", field),
|
|
};
|
|
|
|
fn trim_unit_m(s: &str) -> &str {
|
|
if s.ends_with('m') {
|
|
&s[..s.len() - 1]
|
|
} else {
|
|
s
|
|
}
|
|
}
|
|
|
|
fn parse_precision(s: &str) -> Result<u8> {
|
|
let s = trim_unit_m(s);
|
|
let mut m = 0;
|
|
let mut e = 0;
|
|
let mut dec_point = None;
|
|
for &b in s.as_bytes() {
|
|
if b == b'.' {
|
|
failure::ensure!(
|
|
dec_point.is_none(),
|
|
"invalid precision (double decimal point): {:?}",
|
|
s
|
|
);
|
|
dec_point = Some(0);
|
|
continue;
|
|
}
|
|
failure::ensure!(
|
|
b >= b'0' && b <= b'9',
|
|
"invalid precision (invalid character): {:?}",
|
|
s
|
|
);
|
|
if let Some(ref mut dp) = dec_point {
|
|
if *dp == 2 {
|
|
continue;
|
|
} // ignore following digits
|
|
*dp += 1;
|
|
}
|
|
let d = b - b'0';
|
|
if 0 == m {
|
|
m = d;
|
|
} else {
|
|
e += 1;
|
|
failure::ensure!(e <= 9, "invalid precision (overflow): {:?}", s);
|
|
}
|
|
}
|
|
e += 2 - dec_point.unwrap_or(0);
|
|
failure::ensure!(e <= 9, "invalid precision (overflow): {:?}", s);
|
|
Ok(m << 4 | e)
|
|
}
|
|
|
|
let altitude = match next_field(data) {
|
|
Ok(field) => {
|
|
let f_altitude = trim_unit_m(field).parse::<f64>()?;
|
|
let altitude = (f_altitude * 100.0 + 10000000.0).round() as i64;
|
|
failure::ensure!(
|
|
altitude > 0 && (altitude as u32) as i64 == altitude,
|
|
"altitude out of range"
|
|
);
|
|
altitude as u32
|
|
},
|
|
// standard requires the field, but the example parser doesn't..
|
|
Err(_) => 10000000, // 0m
|
|
};
|
|
|
|
let size = match next_field(data) {
|
|
Ok(field) => parse_precision(field)?,
|
|
Err(_) => 0x12, // => 1e2 cm = 1m
|
|
};
|
|
|
|
let horizontal_precision = match next_field(data) {
|
|
Ok(field) => parse_precision(field)?,
|
|
Err(_) => 0x16, // 1e6 cm = 10km */
|
|
};
|
|
|
|
let vertical_precision = match next_field(data) {
|
|
Ok(field) => parse_precision(field)?,
|
|
Err(_) => 0x13, // 1e3 cm = 10m */
|
|
};
|
|
|
|
Ok(LOC::Version0(LOC0 {
|
|
size,
|
|
horizontal_precision,
|
|
vertical_precision,
|
|
latitude,
|
|
longitude,
|
|
altitude,
|
|
}))
|
|
}
|
|
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
let this = match *self {
|
|
LOC::Version0(ref t) => t,
|
|
_ => return Err(fmt::Error),
|
|
};
|
|
const MAX_LAT_OFFSET: u32 = 3600_000 * 180;
|
|
const MAX_LON_OFFSET: u32 = 3600_000 * 180;
|
|
const LATLON_MID: u32 = 0x8000_0000;
|
|
if this.latitude < LATLON_MID - MAX_LAT_OFFSET
|
|
|| this.latitude > LATLON_MID + MAX_LAT_OFFSET
|
|
{
|
|
return Err(fmt::Error);
|
|
}
|
|
if this.longitude < LATLON_MID - MAX_LON_OFFSET
|
|
|| this.longitude > LATLON_MID + MAX_LON_OFFSET
|
|
{
|
|
return Err(fmt::Error);
|
|
}
|
|
|
|
fn is_invalid_prec(v: u8) -> bool {
|
|
// "leading-digit" << 4 | "exponent(base 10)"
|
|
// if the leading digit is 0, the exponent must be 0 too.
|
|
(v > 0x00 && v < 0x10) || (v >> 4) > 9 || (v & 0xf) > 9
|
|
}
|
|
if is_invalid_prec(this.size)
|
|
|| is_invalid_prec(this.horizontal_precision)
|
|
|| is_invalid_prec(this.vertical_precision)
|
|
{
|
|
return Err(fmt::Error);
|
|
}
|
|
|
|
fn putlatlon2(v: u32, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
let msecs = v % 1000;
|
|
let fullsecs = v / 1000;
|
|
let secs = fullsecs % 60;
|
|
let fullmins = fullsecs / 60;
|
|
let mins = fullmins % 60;
|
|
let deg = fullmins / 60;
|
|
write!(f, "{} {} {}.{:03}", deg, mins, secs, msecs)
|
|
}
|
|
|
|
fn putlatlon(v: u32, f: &mut DnsTextFormatter, pos: char, neg: char) -> fmt::Result {
|
|
if v >= LATLON_MID {
|
|
putlatlon2(v - LATLON_MID, f)?;
|
|
write!(f, "{}", pos)
|
|
} else {
|
|
putlatlon2(LATLON_MID - v, f)?;
|
|
write!(f, "{}", neg)
|
|
}
|
|
}
|
|
|
|
putlatlon(this.latitude, f, 'N', 'S')?;
|
|
putlatlon(this.longitude, f, 'E', 'W')?;
|
|
|
|
write!(f, "{:.2}m", (this.altitude as f64 - 10000000.0) / 100.0)?;
|
|
|
|
fn put_prec(v: u8, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
let m = v >> 4;
|
|
debug_assert!(m < 10);
|
|
let e = v & 0xf;
|
|
if e >= 2 {
|
|
write!(f, "{:0<width$}.00m", m, width = e as usize - 1)
|
|
} else if e == 1 {
|
|
write!(f, ".{}0m", m)
|
|
} else {
|
|
// e == 0
|
|
write!(f, ".0{}m", m)
|
|
}
|
|
}
|
|
|
|
put_prec(this.size, f)?;
|
|
put_prec(this.horizontal_precision, f)?;
|
|
put_prec(this.vertical_precision, f)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData)]
|
|
pub struct LOC0 {
|
|
size: u8,
|
|
horizontal_precision: u8,
|
|
vertical_precision: u8,
|
|
latitude: u32,
|
|
longitude: u32,
|
|
altitude: u32,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug, RRData)]
|
|
#[RRClass(IN)]
|
|
pub struct A6 {
|
|
prefix: u8, // [0...128]
|
|
// might include non-zero padding
|
|
dirty_suffix: Ipv6Addr,
|
|
suffix: Ipv6Addr,
|
|
prefix_name: Option<DnsCanonicalName>,
|
|
}
|
|
|
|
impl DnsPacketData for A6 {
|
|
fn deserialize(data: &mut ::std::io::Cursor<Bytes>) -> Result<Self> {
|
|
let prefix: u8 =
|
|
DnsPacketData::deserialize(data).context("failed parsing field A6::prefix")?;
|
|
failure::ensure!(prefix <= 128, "invalid A6::prefix {}", prefix);
|
|
let suffix_offset = (prefix / 8) as usize;
|
|
debug_assert!(suffix_offset <= 16);
|
|
let suffix_len = 16 - suffix_offset;
|
|
check_enough_data!(data, suffix_len, "A6::suffix");
|
|
let mut addr = [0u8; 16];
|
|
data.read_exact(&mut addr[suffix_offset..16])?;
|
|
|
|
let dirty_suffix = Ipv6Addr::from(addr);
|
|
if suffix_offset < 16 {
|
|
let mask = 0xff >> (prefix % 8);
|
|
addr[suffix_offset] &= mask;
|
|
}
|
|
let suffix = Ipv6Addr::from(addr);
|
|
|
|
let prefix_name = if data.has_remaining() {
|
|
Some(DnsPacketData::deserialize(data)?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
Ok(A6 {
|
|
prefix,
|
|
dirty_suffix,
|
|
suffix,
|
|
prefix_name,
|
|
})
|
|
}
|
|
|
|
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
|
let suffix_offset = (self.prefix / 8) as usize;
|
|
debug_assert!(suffix_offset <= 16);
|
|
let suffix = self.dirty_suffix.octets();
|
|
let suffix_data = &suffix[suffix_offset..];
|
|
packet.reserve(1 /* prefix */ + suffix_data.len());
|
|
packet.put_u8(self.prefix);
|
|
packet.put_slice(suffix_data);
|
|
if let Some(ref n) = self.prefix_name {
|
|
n.serialize(context, packet)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl DnsTextData for A6 {
|
|
fn dns_parse(context: &DnsTextContext, data: &mut &str) -> Result<Self> {
|
|
let prefix: u8 =
|
|
DnsTextData::dns_parse(context, data).context("failed parsing field A6::prefix")?;
|
|
failure::ensure!(prefix <= 128, "invalid A6::prefix {}", prefix);
|
|
|
|
let suffix_offset = (prefix / 8) as usize;
|
|
debug_assert!(suffix_offset <= 16);
|
|
|
|
let suffix: Ipv6Addr =
|
|
DnsTextData::dns_parse(context, data).context("failed parsing field A6::suffix")?;
|
|
|
|
// clear prefix bits
|
|
let mut suffix = suffix.octets();
|
|
for i in 0..suffix_offset {
|
|
suffix[i] = 0;
|
|
}
|
|
if suffix_offset < 16 {
|
|
let mask = 0xff >> (prefix % 8);
|
|
suffix[suffix_offset] &= mask;
|
|
}
|
|
let suffix = Ipv6Addr::from(suffix);
|
|
|
|
let prefix_name = if !data.is_empty() {
|
|
Some(
|
|
DnsTextData::dns_parse(context, data)
|
|
.context("failed parsing field A6::prefix_name")?,
|
|
)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
Ok(A6 {
|
|
prefix,
|
|
dirty_suffix: suffix.clone(),
|
|
suffix,
|
|
prefix_name,
|
|
})
|
|
}
|
|
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
if self.dirty_suffix != self.suffix {
|
|
// parsing text clears the padding, so we would loose data.
|
|
// use binary representation instead.
|
|
return Err(fmt::Error);
|
|
}
|
|
write!(f, "{} {}", self.prefix, self.suffix)?;
|
|
if let Some(ref prefix_name) = self.prefix_name {
|
|
write!(f, "{}", prefix_name)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
pub struct AplItem {
|
|
pub prefix: cidr::IpCidr,
|
|
pub negation: bool,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug, RRData)]
|
|
#[RRClass(IN)]
|
|
pub struct APL {
|
|
items: Vec<AplItem>,
|
|
}
|
|
|
|
impl DnsPacketData for APL {
|
|
fn deserialize(data: &mut ::std::io::Cursor<Bytes>) -> Result<Self> {
|
|
let mut items = Vec::new();
|
|
while data.has_remaining() {
|
|
let family: u16 =
|
|
DnsPacketData::deserialize(data).context("failed parsing APL::ADDRESSFAMILY")?;
|
|
failure::ensure!(
|
|
family == 1 || family == 2,
|
|
"unknown APL::ADDRESSFAMILY {}",
|
|
family
|
|
);
|
|
let prefix: u8 =
|
|
DnsPacketData::deserialize(data).context("failed parsing field APL::PREFIX")?;
|
|
let afd_length: u8 =
|
|
DnsPacketData::deserialize(data).context("failed parsing field APL::AFDLENGTH")?;
|
|
let negation = 0 != (afd_length & 0x80);
|
|
let afd_length = afd_length & 0x7f;
|
|
let data = get_blob(data, afd_length as usize)?;
|
|
|
|
failure::ensure!(
|
|
!data.ends_with(b"\0"),
|
|
"APL::AFDPART ends with trailing zero"
|
|
);
|
|
|
|
let address = if family == 1 {
|
|
failure::ensure!(prefix <= 32, "invalid APL::prefix {} for IPv4", prefix);
|
|
failure::ensure!(
|
|
(afd_length as u32) * 8 < (prefix as u32) + 7,
|
|
"APL::AFDPART too long {} for prefix {}",
|
|
afd_length,
|
|
prefix
|
|
);
|
|
assert!(afd_length <= 4);
|
|
let mut buf = [0u8; 4];
|
|
buf[..data.len()].copy_from_slice(&data);
|
|
std::net::IpAddr::from(std::net::Ipv4Addr::from(buf))
|
|
} else {
|
|
assert!(family == 2);
|
|
failure::ensure!(prefix <= 128, "invalid APL::prefix {} for IPv6", prefix);
|
|
failure::ensure!(
|
|
(afd_length as u32) * 8 < (prefix as u32) + 7,
|
|
"AFD::AFDPART too long {} for prefix {}",
|
|
afd_length,
|
|
prefix
|
|
);
|
|
assert!(afd_length <= 16);
|
|
let mut buf = [0u8; 16];
|
|
buf[..data.len()].copy_from_slice(&data);
|
|
std::net::IpAddr::from(std::net::Ipv6Addr::from(buf))
|
|
};
|
|
use cidr::Cidr;
|
|
let prefix = cidr::IpCidr::new(address, prefix)?;
|
|
|
|
items.push(AplItem { prefix, negation })
|
|
}
|
|
Ok(APL { items })
|
|
}
|
|
|
|
fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
|
use cidr::Cidr;
|
|
for item in &self.items {
|
|
packet.reserve(4);
|
|
packet.put_u16_be(if item.prefix.is_ipv4() { 1 } else { 2 });
|
|
packet.put_u8(item.prefix.network_length());
|
|
let negation_flag = if item.negation { 0x80 } else { 0x00 };
|
|
let mut l = (item.prefix.network_length() + 7) / 8;
|
|
match &item.prefix {
|
|
cidr::IpCidr::V4(p) => {
|
|
let addr = p.first_address().octets();
|
|
while l > 0 && addr[l as usize - 1] == 0 {
|
|
l -= 1;
|
|
}
|
|
packet.put_u8(l | negation_flag);
|
|
packet.extend_from_slice(&addr[..l as usize]);
|
|
},
|
|
cidr::IpCidr::V6(p) => {
|
|
let addr = p.first_address().octets();
|
|
while l > 0 && addr[l as usize - 1] == 0 {
|
|
l -= 1;
|
|
}
|
|
packet.put_u8(l | negation_flag);
|
|
packet.extend_from_slice(&addr[..l as usize]);
|
|
},
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl DnsTextData for APL {
|
|
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> Result<Self> {
|
|
let mut items = Vec::new();
|
|
for item in data.split_ascii_whitespace() {
|
|
let (negation, content) = if item.starts_with('!') {
|
|
(true, &item[1..])
|
|
} else {
|
|
(false, item)
|
|
};
|
|
let (afi, prefix) = match content.find(':') {
|
|
Some(colon) => (&content[..colon], &content[colon + 1..]),
|
|
None => failure::bail!("no colon in APL item: {:?}", item),
|
|
};
|
|
let afi = afi.parse::<u16>()?;
|
|
if content.find('/').is_none() {
|
|
// prefix is mandatory, even if it is /32 for IPv4 or /128 for IPv6.
|
|
failure::bail!("no '/' in APL item: {:?}", item);
|
|
}
|
|
let prefix = match afi {
|
|
1 => prefix.parse::<cidr::Ipv4Cidr>()?.into(),
|
|
2 => prefix.parse::<cidr::Ipv6Cidr>()?.into(),
|
|
_ => failure::bail!("Unknown address family {} in item: {:?}", afi, item),
|
|
};
|
|
items.push(AplItem { prefix, negation });
|
|
}
|
|
*data = "";
|
|
Ok(APL { items })
|
|
}
|
|
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
for item in &self.items {
|
|
f.next_field()?;
|
|
let family = if item.prefix.is_ipv4() { 1 } else { 2 };
|
|
let negation = if item.negation { "!" } else { "" };
|
|
write!(f, "{}{}:{:#}", negation, family, item.prefix)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
pub enum IpsecKeyGateway {
|
|
None,
|
|
Ipv4(Ipv4Addr),
|
|
Ipv6(Ipv6Addr),
|
|
Name(DnsName),
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug, RRData)]
|
|
#[RRClass(ANY)]
|
|
pub enum IPSECKEY {
|
|
Known {
|
|
precedence: u8,
|
|
algorithm: u8,
|
|
gateway: IpsecKeyGateway,
|
|
public_key: Base64RemainingBlob,
|
|
},
|
|
UnknownGateway {
|
|
precedence: u8,
|
|
gateway_type: u8,
|
|
algorithm: u8,
|
|
// length of gateway is unknown, can't split gateway and public key
|
|
remaining: Bytes,
|
|
},
|
|
}
|
|
|
|
impl DnsPacketData for IPSECKEY {
|
|
fn deserialize(data: &mut ::std::io::Cursor<Bytes>) -> Result<Self> {
|
|
let precedence = u8::deserialize(data)?;
|
|
let gateway_type = u8::deserialize(data)?;
|
|
let algorithm = u8::deserialize(data)?;
|
|
let gateway = match gateway_type {
|
|
0 => IpsecKeyGateway::None,
|
|
1 => IpsecKeyGateway::Ipv4(Ipv4Addr::deserialize(data)?),
|
|
2 => IpsecKeyGateway::Ipv6(Ipv6Addr::deserialize(data)?),
|
|
3 => IpsecKeyGateway::Name(DnsName::deserialize(data)?),
|
|
_ => {
|
|
return Ok(IPSECKEY::UnknownGateway {
|
|
precedence,
|
|
gateway_type,
|
|
algorithm,
|
|
remaining: remaining_bytes(data),
|
|
})
|
|
},
|
|
};
|
|
Ok(IPSECKEY::Known {
|
|
precedence,
|
|
algorithm,
|
|
gateway,
|
|
public_key: Base64RemainingBlob::deserialize(data)?,
|
|
})
|
|
}
|
|
|
|
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
|
match *self {
|
|
IPSECKEY::Known {
|
|
precedence,
|
|
algorithm,
|
|
ref gateway,
|
|
ref public_key,
|
|
} => {
|
|
packet.reserve(3);
|
|
packet.put_u8(precedence);
|
|
let gateway_type: u8 = match *gateway {
|
|
IpsecKeyGateway::None => 0,
|
|
IpsecKeyGateway::Ipv4(_) => 1,
|
|
IpsecKeyGateway::Ipv6(_) => 2,
|
|
IpsecKeyGateway::Name(_) => 3,
|
|
};
|
|
packet.put_u8(gateway_type);
|
|
packet.put_u8(algorithm);
|
|
match *gateway {
|
|
IpsecKeyGateway::None => (),
|
|
IpsecKeyGateway::Ipv4(ref a) => a.serialize(context, packet)?,
|
|
IpsecKeyGateway::Ipv6(ref a) => a.serialize(context, packet)?,
|
|
IpsecKeyGateway::Name(ref n) => n.serialize(context, packet)?,
|
|
};
|
|
public_key.serialize(context, packet)?;
|
|
},
|
|
IPSECKEY::UnknownGateway {
|
|
precedence,
|
|
gateway_type,
|
|
algorithm,
|
|
ref remaining,
|
|
} => {
|
|
packet.reserve(3 + remaining.len());
|
|
packet.put_u8(precedence);
|
|
packet.put_u8(gateway_type);
|
|
packet.put_u8(algorithm);
|
|
packet.put_slice(remaining);
|
|
},
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl DnsTextData for IPSECKEY {
|
|
fn dns_parse(context: &DnsTextContext, data: &mut &str) -> Result<Self> {
|
|
let precedence = u8::dns_parse(context, data)?;
|
|
let gateway_type = u8::dns_parse(context, data)?;
|
|
let algorithm = u8::dns_parse(context, data)?;
|
|
let gateway = match gateway_type {
|
|
0 => IpsecKeyGateway::None,
|
|
1 => IpsecKeyGateway::Ipv4(Ipv4Addr::dns_parse(context, data)?),
|
|
2 => IpsecKeyGateway::Ipv6(Ipv6Addr::dns_parse(context, data)?),
|
|
3 => IpsecKeyGateway::Name(DnsName::dns_parse(context, data)?),
|
|
_ => failure::bail!("unknown gateway type {} for IPSECKEY", gateway_type),
|
|
};
|
|
Ok(IPSECKEY::Known {
|
|
precedence,
|
|
algorithm,
|
|
gateway,
|
|
public_key: Base64RemainingBlob::dns_parse(context, data)?,
|
|
})
|
|
}
|
|
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
match *self {
|
|
IPSECKEY::Known {
|
|
precedence,
|
|
algorithm,
|
|
ref gateway,
|
|
ref public_key,
|
|
} => {
|
|
let gateway_type: u8 = match *gateway {
|
|
IpsecKeyGateway::None => 0,
|
|
IpsecKeyGateway::Ipv4(_) => 1,
|
|
IpsecKeyGateway::Ipv6(_) => 2,
|
|
IpsecKeyGateway::Name(_) => 3,
|
|
};
|
|
write!(f, "{} {} {}", precedence, gateway_type, algorithm)?;
|
|
match *gateway {
|
|
IpsecKeyGateway::None => (),
|
|
IpsecKeyGateway::Ipv4(ref a) => a.dns_format(f)?,
|
|
IpsecKeyGateway::Ipv6(ref a) => a.dns_format(f)?,
|
|
IpsecKeyGateway::Name(ref n) => n.dns_format(f)?,
|
|
};
|
|
public_key.dns_format(f)?;
|
|
Ok(())
|
|
},
|
|
IPSECKEY::UnknownGateway { .. } => Err(fmt::Error),
|
|
}
|
|
}
|
|
}
|