Skip to main content

ntp_proto/protocol/
io.rs

1use byteorder::{BE, ReadBytesExt, WriteBytesExt};
2use std::io;
3
4#[cfg(feature = "ntpv5")]
5use super::ntpv5::{NtpV5Flags, PacketV5, Time32, Timescale};
6use super::{
7    DateFormat, KissOfDeath, LeapIndicator, Mode, Packet, PrimarySource, ReadBytes, ReadFromBytes,
8    ReferenceIdentifier, ShortFormat, Stratum, TimestampFormat, Version, WriteBytes, WriteToBytes,
9    be_u32_to_bytes,
10};
11
12// Writer implementations.
13
14impl<W> WriteBytes for W
15where
16    W: WriteBytesExt,
17{
18    fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()> {
19        protocol.write_to_bytes(self)
20    }
21}
22
23impl<P> WriteToBytes for &P
24where
25    P: WriteToBytes,
26{
27    fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()> {
28        (*self).write_to_bytes(writer)
29    }
30}
31
32impl WriteToBytes for ShortFormat {
33    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
34        writer.write_u16::<BE>(self.seconds)?;
35        writer.write_u16::<BE>(self.fraction)?;
36        Ok(())
37    }
38}
39
40impl WriteToBytes for TimestampFormat {
41    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
42        writer.write_u32::<BE>(self.seconds)?;
43        writer.write_u32::<BE>(self.fraction)?;
44        Ok(())
45    }
46}
47
48impl WriteToBytes for DateFormat {
49    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
50        writer.write_i32::<BE>(self.era_number)?;
51        writer.write_u32::<BE>(self.era_offset)?;
52        writer.write_u64::<BE>(self.fraction)?;
53        Ok(())
54    }
55}
56
57impl WriteToBytes for Stratum {
58    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
59        writer.write_u8(self.0)?;
60        Ok(())
61    }
62}
63
64impl WriteToBytes for ReferenceIdentifier {
65    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
66        match *self {
67            ReferenceIdentifier::KissOfDeath(kod) => {
68                writer.write_u32::<BE>(kod as u32)?;
69            }
70            ReferenceIdentifier::PrimarySource(src) => {
71                writer.write_u32::<BE>(src as u32)?;
72            }
73            ReferenceIdentifier::SecondaryOrClient(arr) => {
74                writer.write_u32::<BE>(code_to_u32!(&arr))?;
75            }
76            ReferenceIdentifier::Unknown(arr) => {
77                writer.write_u32::<BE>(code_to_u32!(&arr))?;
78            }
79        }
80        Ok(())
81    }
82}
83
84impl WriteToBytes for (LeapIndicator, Version, Mode) {
85    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
86        let (li, vn, mode) = *self;
87        let mut li_vn_mode = 0;
88        li_vn_mode |= (li as u8) << 6;
89        li_vn_mode |= vn.0 << 3;
90        li_vn_mode |= mode as u8;
91        writer.write_u8(li_vn_mode)?;
92        Ok(())
93    }
94}
95
96impl WriteToBytes for Packet {
97    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
98        let li_vn_mode = (self.leap_indicator, self.version, self.mode);
99        writer.write_bytes(li_vn_mode)?;
100        writer.write_bytes(self.stratum)?;
101        writer.write_i8(self.poll)?;
102        writer.write_i8(self.precision)?;
103        writer.write_bytes(self.root_delay)?;
104        writer.write_bytes(self.root_dispersion)?;
105        writer.write_bytes(self.reference_id)?;
106        writer.write_bytes(self.reference_timestamp)?;
107        writer.write_bytes(self.origin_timestamp)?;
108        writer.write_bytes(self.receive_timestamp)?;
109        writer.write_bytes(self.transmit_timestamp)?;
110        Ok(())
111    }
112}
113
114// Reader implementations.
115
116impl<R> ReadBytes for R
117where
118    R: ReadBytesExt,
119{
120    fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P> {
121        P::read_from_bytes(self)
122    }
123}
124
125impl ReadFromBytes for ShortFormat {
126    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
127        let seconds = reader.read_u16::<BE>()?;
128        let fraction = reader.read_u16::<BE>()?;
129        let short_format = ShortFormat { seconds, fraction };
130        Ok(short_format)
131    }
132}
133
134impl ReadFromBytes for TimestampFormat {
135    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
136        let seconds = reader.read_u32::<BE>()?;
137        let fraction = reader.read_u32::<BE>()?;
138        let timestamp_format = TimestampFormat { seconds, fraction };
139        Ok(timestamp_format)
140    }
141}
142
143impl ReadFromBytes for DateFormat {
144    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
145        let era_number = reader.read_i32::<BE>()?;
146        let era_offset = reader.read_u32::<BE>()?;
147        let fraction = reader.read_u64::<BE>()?;
148        let date_format = DateFormat {
149            era_number,
150            era_offset,
151            fraction,
152        };
153        Ok(date_format)
154    }
155}
156
157impl ReadFromBytes for Stratum {
158    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
159        let stratum = Stratum(reader.read_u8()?);
160        Ok(stratum)
161    }
162}
163
164impl ReadFromBytes for (LeapIndicator, Version, Mode) {
165    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
166        let li_vn_mode = reader.read_u8()?;
167        let li_u8 = li_vn_mode >> 6;
168        let vn_u8 = (li_vn_mode >> 3) & 0b111;
169        let mode_u8 = li_vn_mode & 0b111;
170        let li = match LeapIndicator::try_from(li_u8).ok() {
171            Some(li) => li,
172            None => {
173                let err_msg = "unknown leap indicator";
174                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
175            }
176        };
177        let vn = Version(vn_u8);
178        let mode = match Mode::try_from(mode_u8).ok() {
179            Some(mode) => mode,
180            None => {
181                let err_msg = "unknown association mode";
182                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
183            }
184        };
185        Ok((li, vn, mode))
186    }
187}
188
189impl ReadFromBytes for Packet {
190    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
191        let (leap_indicator, version, mode) = reader.read_bytes()?;
192        let stratum = reader.read_bytes::<Stratum>()?;
193        let poll = reader.read_i8()?;
194        let precision = reader.read_i8()?;
195        let root_delay = reader.read_bytes()?;
196        let root_dispersion = reader.read_bytes()?;
197        let reference_id = {
198            let u = reader.read_u32::<BE>()?;
199            let raw_bytes = be_u32_to_bytes(u);
200            if stratum == Stratum::UNSPECIFIED {
201                // Stratum 0: Kiss-o'-Death packet (RFC 5905 Section 7.4).
202                match KissOfDeath::try_from(u) {
203                    Ok(kod) => ReferenceIdentifier::KissOfDeath(kod),
204                    Err(_) => ReferenceIdentifier::Unknown(raw_bytes),
205                }
206            } else if stratum == Stratum::PRIMARY {
207                // Stratum 1: primary reference source (4-char ASCII).
208                match PrimarySource::try_from(u) {
209                    Ok(src) => ReferenceIdentifier::PrimarySource(src),
210                    Err(_) => ReferenceIdentifier::Unknown(raw_bytes),
211                }
212            } else if stratum.is_secondary() {
213                // Stratum 2-15: IPv4 address or first 4 octets of MD5 hash of IPv6 address.
214                ReferenceIdentifier::SecondaryOrClient(raw_bytes)
215            } else {
216                // Stratum 16 (unsynchronized) or 17-255 (reserved).
217                ReferenceIdentifier::Unknown(raw_bytes)
218            }
219        };
220        let reference_timestamp = reader.read_bytes()?;
221        let origin_timestamp = reader.read_bytes()?;
222        let receive_timestamp = reader.read_bytes()?;
223        let transmit_timestamp = reader.read_bytes()?;
224        Ok(Packet {
225            leap_indicator,
226            version,
227            mode,
228            stratum,
229            poll,
230            precision,
231            root_delay,
232            root_dispersion,
233            reference_id,
234            reference_timestamp,
235            origin_timestamp,
236            receive_timestamp,
237            transmit_timestamp,
238        })
239    }
240}
241
242// ============================================================================
243// NTPv5 ReadFromBytes / WriteToBytes implementations
244// ============================================================================
245
246#[cfg(feature = "ntpv5")]
247impl WriteToBytes for Time32 {
248    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
249        writer.write_u32::<BE>(self.0)?;
250        Ok(())
251    }
252}
253
254#[cfg(feature = "ntpv5")]
255impl ReadFromBytes for Time32 {
256    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
257        let raw = reader.read_u32::<BE>()?;
258        Ok(Time32(raw))
259    }
260}
261
262#[cfg(feature = "ntpv5")]
263impl WriteToBytes for Timescale {
264    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
265        writer.write_u8(*self as u8)?;
266        Ok(())
267    }
268}
269
270#[cfg(feature = "ntpv5")]
271impl ReadFromBytes for Timescale {
272    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
273        let raw = reader.read_u8()?;
274        Timescale::try_from(raw)
275            .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "unknown timescale value"))
276    }
277}
278
279#[cfg(feature = "ntpv5")]
280impl WriteToBytes for NtpV5Flags {
281    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
282        writer.write_u16::<BE>(self.0)?;
283        Ok(())
284    }
285}
286
287#[cfg(feature = "ntpv5")]
288impl ReadFromBytes for NtpV5Flags {
289    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
290        let raw = reader.read_u16::<BE>()?;
291        Ok(NtpV5Flags(raw))
292    }
293}
294
295#[cfg(feature = "ntpv5")]
296impl WriteToBytes for PacketV5 {
297    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
298        let li_vn_mode = (self.leap_indicator, self.version, self.mode);
299        writer.write_bytes(li_vn_mode)?;
300        writer.write_bytes(self.stratum)?;
301        writer.write_i8(self.poll)?;
302        writer.write_i8(self.precision)?;
303        writer.write_bytes(self.root_delay)?;
304        writer.write_bytes(self.root_dispersion)?;
305        writer.write_bytes(self.timescale)?;
306        writer.write_u8(self.era)?;
307        writer.write_bytes(self.flags)?;
308        writer.write_u64::<BE>(self.server_cookie)?;
309        writer.write_u64::<BE>(self.client_cookie)?;
310        writer.write_bytes(self.receive_timestamp)?;
311        writer.write_bytes(self.transmit_timestamp)?;
312        Ok(())
313    }
314}
315
316#[cfg(feature = "ntpv5")]
317impl ReadFromBytes for PacketV5 {
318    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
319        let (leap_indicator, version, mode) = reader.read_bytes()?;
320        let stratum = reader.read_bytes::<Stratum>()?;
321        let poll = reader.read_i8()?;
322        let precision = reader.read_i8()?;
323        let root_delay = reader.read_bytes::<Time32>()?;
324        let root_dispersion = reader.read_bytes::<Time32>()?;
325        let timescale = reader.read_bytes::<Timescale>()?;
326        let era = reader.read_u8()?;
327        let flags = reader.read_bytes::<NtpV5Flags>()?;
328        let server_cookie = reader.read_u64::<BE>()?;
329        let client_cookie = reader.read_u64::<BE>()?;
330        let receive_timestamp = reader.read_bytes()?;
331        let transmit_timestamp = reader.read_bytes()?;
332        Ok(PacketV5 {
333            leap_indicator,
334            version,
335            mode,
336            stratum,
337            poll,
338            precision,
339            root_delay,
340            root_dispersion,
341            timescale,
342            era,
343            flags,
344            server_cookie,
345            client_cookie,
346            receive_timestamp,
347            transmit_timestamp,
348        })
349    }
350}