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) -> Result { 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) -> 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 { let degrees_latitude = next_field(data)?.parse::()?; 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::()?; 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::()?; 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::()?; 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::()?; 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::()?; 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 { 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::()?; 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, } impl DnsPacketData for A6 { fn deserialize(data: &mut ::std::io::Cursor) -> Result { 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) -> 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 { 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, } impl DnsPacketData for APL { fn deserialize(data: &mut ::std::io::Cursor) -> Result { 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) -> 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 { 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::()?; 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::()?.into(), 2 => prefix.parse::()?.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) -> Result { 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) -> 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 { 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), } } }