ntp_client/
packet.rs

1use crate::formats::timestamp::{ShortFormat, TimestampFormat};
2use crate::formats::{LeapIndicator, Mode, PrimarySource, ReferenceIdentifier, Stratum, Version};
3use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
4
5#[derive(Debug, PartialEq, Default, Clone)]
6pub struct Packet {
7    pub li: LeapIndicator,
8    pub vn: Version,
9    pub mode: Mode,
10    pub stratum: Stratum,
11    pub poll: i8, // Is this unsigned?
12    pub precision: i8,
13    pub delay: ShortFormat,
14    pub dispersion: ShortFormat,
15    pub ref_id: ReferenceIdentifier,
16    pub ref_time: TimestampFormat,
17    pub orig_time: TimestampFormat,
18    pub recv_time: TimestampFormat,
19    pub transmit_time: TimestampFormat,
20}
21
22impl Packet {
23    pub fn new_client() -> Packet {
24        Packet {
25            mode: Mode::Client,
26            vn: Version::Ver2,
27            transmit_time: TimestampFormat::now(),
28            ..Default::default()
29        }
30    }
31
32    pub fn try_from<T: ReadBytesExt>(mut rdr: T) -> e_utils::AnyResult<Packet> {
33        let li_vn_mode = rdr.read_u8()?;
34        let li = LeapIndicator::from_repr(li_vn_mode >> 6).ok_or("From LeapIndicator")?;
35        let vn = Version::from_repr((li_vn_mode >> 3) & 0b111).ok_or("From Version")?;
36        let mode = Mode::from_repr(li_vn_mode & 0b111).ok_or("From Mode")?;
37
38        let stratum = Stratum::new(rdr.read_u8()?);
39
40        let poll = rdr.read_i8()?;
41        let precision = rdr.read_i8()?;
42        let delay = rdr.read_u32::<NetworkEndian>()?.into();
43        let dispersion = rdr.read_u32::<NetworkEndian>()?.into();
44        let ref_id_raw = rdr.read_u32::<NetworkEndian>()?.into();
45        let ref_id = if stratum.primary() {
46            let source = PrimarySource::from_repr(ref_id_raw).ok_or("From PrimarySource")?;
47            ReferenceIdentifier::Primary(source)
48        } else if stratum.secondary() {
49            ReferenceIdentifier::Secondary(ref_id_raw)
50        } else {
51            return Err(format!("Unsupported stratum: {}", stratum.get_value()).into());
52        };
53        let ref_time = rdr.read_u64::<NetworkEndian>()?.into();
54        let orig_time = rdr.read_u64::<NetworkEndian>()?.into();
55        let recv_time = rdr.read_u64::<NetworkEndian>()?.into();
56        let transmit_time = rdr.read_u64::<NetworkEndian>()?.into();
57
58        Ok(Packet {
59            li: li,
60            vn: vn,
61            mode: mode,
62            stratum: stratum,
63            poll: poll,
64            precision: precision,
65            delay: delay,
66            dispersion: dispersion,
67            ref_id: ref_id,
68            ref_time: ref_time,
69            orig_time: orig_time,
70            recv_time: recv_time,
71            transmit_time: transmit_time,
72        })
73    }
74}
75
76impl From<Packet> for Vec<u8> {
77    fn from(p: Packet) -> Vec<u8> {
78        let mut buf = Vec::with_capacity(48);
79        let mut li_vn_mode = 0;
80        li_vn_mode |= (p.li as u8) << 6;
81        li_vn_mode |= (p.vn as u8) << 3;
82        li_vn_mode |= p.mode as u8;
83        buf.push(li_vn_mode);
84        buf.push(p.stratum.get_value());
85        buf.push(p.poll as u8);
86        buf.push(p.precision as u8);
87        buf.write_u32::<NetworkEndian>(p.delay.into())
88            .expect("can't fail");
89        buf.write_u32::<NetworkEndian>(p.dispersion.into())
90            .expect("can't fail");
91        buf.write_u32::<NetworkEndian>(p.ref_id.into())
92            .expect("can't fail");
93        buf.write_u64::<NetworkEndian>(p.ref_time.into())
94            .expect("can't fail");
95        buf.write_u64::<NetworkEndian>(p.orig_time.into())
96            .expect("can't fail");
97        buf.write_u64::<NetworkEndian>(p.recv_time.into())
98            .expect("can't fail");
99        buf.write_u64::<NetworkEndian>(p.transmit_time.into())
100            .expect("can't fail");
101        buf
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::Packet;
108    use crate::formats::timestamp::{ShortFormat, TimestampFormat};
109    use crate::formats::{
110        LeapIndicator, Mode, PrimarySource, ReferenceIdentifier, Stratum, Version,
111    };
112    use std::io::Cursor;
113
114    #[test]
115    fn packet_from_bytes() {
116        let input = vec![
117            20u8, 1, 3, 240, 0, 0, 0, 0, 0, 0, 0, 24, 67, 68, 77, 65, 215, 188, 128, 105, 198, 169,
118            46, 99, 215, 187, 177, 194, 159, 47, 120, 0, 215, 188, 128, 113, 45, 236, 230, 45, 215,
119            188, 128, 113, 46, 35, 158, 108,
120        ];
121        let expected_output = Packet {
122            li: LeapIndicator::NoWarning,
123            vn: Version::Ver2,
124            mode: Mode::Server,
125            stratum: Stratum::new(1),
126            poll: 3,
127            precision: -16,
128            delay: ShortFormat { sec: 0, frac: 0 },
129            dispersion: ShortFormat { sec: 0, frac: 24 },
130            ref_id: ReferenceIdentifier::Primary(PrimarySource::CDMA),
131            ref_time: TimestampFormat {
132                sec: 3619455081,
133                frac: 3332976227,
134            },
135            orig_time: TimestampFormat {
136                sec: 3619402178,
137                frac: 2670688256,
138            },
139            recv_time: TimestampFormat {
140                sec: 3619455089,
141                frac: 770500141,
142            },
143            transmit_time: TimestampFormat {
144                sec: 3619455089,
145                frac: 774086252,
146            },
147        };
148
149        let rdr = Cursor::new(input);
150        assert_eq!(expected_output, Packet::try_from(rdr).unwrap());
151    }
152
153    #[test]
154    fn packet_to_bytes() {
155        let expected_output = vec![
156            20, 1, 3, 240, 0, 0, 0, 0, 0, 0, 0, 24, 67, 68, 77, 65, 215, 188, 128, 105, 198, 169,
157            46, 99, 215, 187, 177, 194, 159, 47, 120, 0, 215, 188, 128, 113, 45, 236, 230, 45, 215,
158            188, 128, 113, 46, 35, 158, 108,
159        ];
160        let input = Packet {
161            li: LeapIndicator::NoWarning,
162            vn: Version::Ver2,
163            mode: Mode::Server,
164            stratum: Stratum::new(1),
165            poll: 3,
166            precision: -16,
167            delay: ShortFormat { sec: 0, frac: 0 },
168            dispersion: ShortFormat { sec: 0, frac: 24 },
169            ref_id: ReferenceIdentifier::Primary(PrimarySource::CDMA),
170            ref_time: TimestampFormat {
171                sec: 3619455081,
172                frac: 3332976227,
173            },
174            orig_time: TimestampFormat {
175                sec: 3619402178,
176                frac: 2670688256,
177            },
178            recv_time: TimestampFormat {
179                sec: 3619455089,
180                frac: 770500141,
181            },
182            transmit_time: TimestampFormat {
183                sec: 3619455089,
184                frac: 774086252,
185            },
186        };
187        let output: Vec<u8> = input.into();
188        assert_eq!(output, expected_output);
189    }
190
191    #[test]
192    fn packet_conversion_roundtrip() {
193        let input = vec![
194            20, 1, 3, 240, 0, 0, 0, 0, 0, 0, 0, 24, 67, 68, 77, 65, 215, 188, 128, 105, 198, 169,
195            46, 99, 215, 187, 177, 194, 159, 47, 120, 0, 215, 188, 128, 113, 45, 236, 230, 45, 215,
196            188, 128, 113, 46, 35, 158, 108,
197        ];
198        let rdr = Cursor::new(&input);
199        let output: Vec<u8> = Packet::try_from(rdr).unwrap().into();
200        assert_eq!(input.as_slice(), output.as_slice());
201    }
202}