simple_dns/dns/rdata/
loc.rs

1use crate::{bytes_buffer::BytesBuffer, dns::WireFormat, lib::Write, SimpleDnsError};
2
3use super::RR;
4
5///  A Means for Expressing Location Information in the Domain Name System [RFC 1876](https://datatracker.ietf.org/doc/html/rfc1876)
6#[derive(Debug, PartialEq, Eq, Hash, Clone)]
7pub struct LOC {
8    /// Version number of the representation.  This must be zero.
9    pub version: u8,
10    /// The diameter of a sphere enclosing the described entity, in centimeters, expressed as a pair of four-bit unsigned integers
11    pub size: u8,
12    /// The horizontal precision of the data, in centimeters, expressed using the same representation as SIZE
13    pub horizontal_precision: u8,
14    /// The vertical precision of the data, in centimeters, expressed using the sane representation as for SIZE
15    pub vertical_precision: u8,
16    /// The latitude of the center of the sphere described by the SIZE field
17    pub latitude: i32,
18    /// The longitude of the center of the sphere described by the SIZE field
19    pub longitude: i32,
20    /// The altitude of the center of the sphere described by the SIZE field
21    pub altitude: i32,
22}
23
24impl RR for LOC {
25    const TYPE_CODE: u16 = 29;
26}
27
28impl LOC {
29    /// Transforms the inner data into its owned type
30    pub fn into_owned(self) -> Self {
31        self
32    }
33}
34
35impl<'a> WireFormat<'a> for LOC {
36    const MINIMUM_LEN: usize = 16;
37
38    fn parse(data: &mut BytesBuffer<'a>) -> crate::Result<Self>
39    where
40        Self: Sized,
41    {
42        let version = data.get_u8()?;
43        if version != 0 {
44            return Err(SimpleDnsError::InvalidDnsPacket);
45        }
46
47        let size = data.get_u8()?;
48        let horizontal_precision = data.get_u8()?;
49        let vertical_precision = data.get_u8()?;
50        let latitude = data.get_i32()?;
51        let longitude = data.get_i32()?;
52        let altitude = data.get_i32()?;
53
54        Ok(LOC {
55            version,
56            size,
57            horizontal_precision,
58            vertical_precision,
59            latitude,
60            longitude,
61            altitude,
62        })
63    }
64
65    fn write_to<T: Write>(&self, out: &mut T) -> crate::Result<()> {
66        if self.version != 0 {
67            return Err(SimpleDnsError::InvalidDnsPacket);
68        }
69
70        out.write_all(&[
71            self.version.to_be(),
72            self.size.to_be(),
73            self.horizontal_precision.to_be(),
74            self.vertical_precision.to_be(),
75        ])?;
76        out.write_all(&self.latitude.to_be_bytes())?;
77        out.write_all(&self.longitude.to_be_bytes())?;
78        out.write_all(&self.altitude.to_be_bytes())?;
79
80        Ok(())
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use crate::lib::Vec;
88
89    #[test]
90    fn parse_and_write_loc() {
91        let loc = LOC {
92            version: 0,
93            size: 0x10,
94            vertical_precision: 0x11,
95            horizontal_precision: 0x12,
96            altitude: 1000,
97            longitude: 2000,
98            latitude: 3000,
99        };
100
101        let mut data = Vec::new();
102        assert!(loc.write_to(&mut data).is_ok());
103
104        let loc = LOC::parse(&mut (&data[..]).into());
105        assert!(loc.is_ok());
106        let loc = loc.unwrap();
107
108        assert_eq!(0x10, loc.size);
109        assert_eq!(0x11, loc.vertical_precision);
110        assert_eq!(0x12, loc.horizontal_precision);
111        assert_eq!(1000, loc.altitude);
112        assert_eq!(2000, loc.longitude);
113        assert_eq!(3000, loc.latitude);
114
115        assert_eq!(data.len(), loc.len());
116    }
117
118    #[test]
119    #[cfg(feature = "std")]
120    fn parse_sample() -> Result<(), Box<dyn std::error::Error>> {
121        use crate::{rdata::RData, ResourceRecord};
122        let sample_file = std::fs::read("samples/zonefile/LOC.sample")?;
123
124        let sample_rdata = match ResourceRecord::parse(&mut (&sample_file[..]).into())?.rdata {
125            RData::LOC(rdata) => rdata,
126            _ => unreachable!(),
127        };
128
129        // 60 09 00.000 N 24 39 00.000 E 10.00m 20.00m ( 2000.00m 20.00m )
130        assert_eq!(35, sample_rdata.size);
131        assert_eq!(35, sample_rdata.vertical_precision);
132        assert_eq!(37, sample_rdata.horizontal_precision);
133        assert_eq!(10001000, sample_rdata.altitude);
134        assert_eq!(-2058743648, sample_rdata.longitude);
135        assert_eq!(-1930943648, sample_rdata.latitude);
136        Ok(())
137    }
138}