You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
699 lines
19 KiB
699 lines
19 KiB
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), |
|
} |
|
} |
|
}
|
|
|