|
|
|
@ -3,7 +3,7 @@ use errors::*;
|
|
|
|
|
use common_types::*;
|
|
|
|
|
use failure::ResultExt;
|
|
|
|
|
use ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes};
|
|
|
|
|
use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext};
|
|
|
|
|
use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field};
|
|
|
|
|
use std::fmt;
|
|
|
|
|
use std::io::Read;
|
|
|
|
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
|
|
|
@ -53,13 +53,189 @@ impl DnsPacketData for LOC {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl DnsTextData for LOC {
|
|
|
|
|
fn dns_parse(_context: &DnsTextContext, _data: &mut &str) -> Result<Self> {
|
|
|
|
|
unimplemented!()
|
|
|
|
|
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> Result<Self> {
|
|
|
|
|
let degrees_latitude = next_field(data)?.parse::<u8>()?;
|
|
|
|
|
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>()?;
|
|
|
|
|
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>()?;
|
|
|
|
|
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;
|
|
|
|
|
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,
|
|
|
|
|
_ => bail!("invalid latitude orientation [NS]: {}", field),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let degrees_longitude = next_field(data)?.parse::<u8>()?;
|
|
|
|
|
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>()?;
|
|
|
|
|
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>()?;
|
|
|
|
|
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;
|
|
|
|
|
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,
|
|
|
|
|
_ => 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'.' {
|
|
|
|
|
ensure!(dec_point.is_none(), "invalid precision (double decimal point): {:?}", s);
|
|
|
|
|
dec_point = Some(0);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
ensure!(e <= 9, "invalid precision (overflow): {:?}", s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
e += 2 - dec_point.unwrap_or(0);
|
|
|
|
|
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;
|
|
|
|
|
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 {
|
|
|
|
|
// always prefer binary representation
|
|
|
|
|
Err(fmt::Error)
|
|
|
|
|
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(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|