simple_dns/dns/rdata/
loc.rs

1use crate::{dns::WireFormat, 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    fn parse(data: &'a [u8], position: &mut usize) -> crate::Result<Self>
37    where
38        Self: Sized,
39    {
40        if data.len() < *position + 16 {
41            return Err(SimpleDnsError::InsufficientData);
42        }
43
44        let data = &data[*position..*position + 16];
45        *position += 16;
46
47        let version = u8::from_be(data[0]);
48        if version != 0 {
49            return Err(SimpleDnsError::InvalidDnsPacket);
50        }
51
52        let size = u8::from_be(data[1]);
53        let horizontal_precision = u8::from_be(data[2]);
54        let vertical_precision = u8::from_be(data[3]);
55        let latitude = i32::from_be_bytes(data[4..8].try_into()?);
56        let longitude = i32::from_be_bytes(data[8..12].try_into()?);
57        let altitude = i32::from_be_bytes(data[12..16].try_into()?);
58
59        Ok(LOC {
60            version,
61            size,
62            horizontal_precision,
63            vertical_precision,
64            latitude,
65            longitude,
66            altitude,
67        })
68    }
69
70    fn write_to<T: std::io::Write>(&self, out: &mut T) -> crate::Result<()> {
71        if self.version != 0 {
72            return Err(SimpleDnsError::InvalidDnsPacket);
73        }
74
75        out.write_all(&[
76            self.version.to_be(),
77            self.size.to_be(),
78            self.horizontal_precision.to_be(),
79            self.vertical_precision.to_be(),
80        ])?;
81        out.write_all(&self.latitude.to_be_bytes())?;
82        out.write_all(&self.longitude.to_be_bytes())?;
83        out.write_all(&self.altitude.to_be_bytes())?;
84
85        Ok(())
86    }
87
88    fn len(&self) -> usize {
89        16
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use crate::{rdata::RData, ResourceRecord};
96
97    use super::*;
98
99    #[test]
100    fn parse_and_write_loc() {
101        let loc = LOC {
102            version: 0,
103            size: 0x10,
104            vertical_precision: 0x11,
105            horizontal_precision: 0x12,
106            altitude: 1000,
107            longitude: 2000,
108            latitude: 3000,
109        };
110
111        let mut data = Vec::new();
112        assert!(loc.write_to(&mut data).is_ok());
113
114        let loc = LOC::parse(&data, &mut 0);
115        assert!(loc.is_ok());
116        let loc = loc.unwrap();
117
118        assert_eq!(0x10, loc.size);
119        assert_eq!(0x11, loc.vertical_precision);
120        assert_eq!(0x12, loc.horizontal_precision);
121        assert_eq!(1000, loc.altitude);
122        assert_eq!(2000, loc.longitude);
123        assert_eq!(3000, loc.latitude);
124
125        assert_eq!(data.len(), loc.len());
126    }
127
128    #[test]
129    fn parse_sample() -> Result<(), Box<dyn std::error::Error>> {
130        let sample_file = std::fs::read("samples/zonefile/LOC.sample")?;
131
132        let sample_rdata = match ResourceRecord::parse(&sample_file, &mut 0)?.rdata {
133            RData::LOC(rdata) => rdata,
134            _ => unreachable!(),
135        };
136
137        // 60 09 00.000 N 24 39 00.000 E 10.00m 20.00m ( 2000.00m 20.00m )
138        assert_eq!(35, sample_rdata.size);
139        assert_eq!(35, sample_rdata.vertical_precision);
140        assert_eq!(37, sample_rdata.horizontal_precision);
141        assert_eq!(10001000, sample_rdata.altitude);
142        assert_eq!(-2058743648, sample_rdata.longitude);
143        assert_eq!(-1930943648, sample_rdata.latitude);
144        Ok(())
145    }
146}