Skip to main content

ntp_proto/protocol/
bytes.rs

1use crate::error::ParseError;
2
3use super::{
4    ConstPackedSizeBytes, DateFormat, FromBytes, KissOfDeath, LeapIndicator, Mode, Packet,
5    PrimarySource, ReferenceIdentifier, ShortFormat, Stratum, TimestampFormat, ToBytes, Version,
6};
7
8impl FromBytes for ShortFormat {
9    fn from_bytes(buf: &[u8]) -> Result<(Self, usize), ParseError> {
10        if buf.len() < Self::PACKED_SIZE_BYTES {
11            return Err(ParseError::BufferTooShort {
12                needed: Self::PACKED_SIZE_BYTES,
13                available: buf.len(),
14            });
15        }
16        let seconds = u16::from_be_bytes([buf[0], buf[1]]);
17        let fraction = u16::from_be_bytes([buf[2], buf[3]]);
18        Ok((ShortFormat { seconds, fraction }, Self::PACKED_SIZE_BYTES))
19    }
20}
21
22impl FromBytes for TimestampFormat {
23    fn from_bytes(buf: &[u8]) -> Result<(Self, usize), ParseError> {
24        if buf.len() < Self::PACKED_SIZE_BYTES {
25            return Err(ParseError::BufferTooShort {
26                needed: Self::PACKED_SIZE_BYTES,
27                available: buf.len(),
28            });
29        }
30        let seconds = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]);
31        let fraction = u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]);
32        Ok((
33            TimestampFormat { seconds, fraction },
34            Self::PACKED_SIZE_BYTES,
35        ))
36    }
37}
38
39impl FromBytes for DateFormat {
40    fn from_bytes(buf: &[u8]) -> Result<(Self, usize), ParseError> {
41        if buf.len() < Self::PACKED_SIZE_BYTES {
42            return Err(ParseError::BufferTooShort {
43                needed: Self::PACKED_SIZE_BYTES,
44                available: buf.len(),
45            });
46        }
47        let era_number = i32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]);
48        let era_offset = u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]);
49        let fraction = u64::from_be_bytes([
50            buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15],
51        ]);
52        Ok((
53            DateFormat {
54                era_number,
55                era_offset,
56                fraction,
57            },
58            Self::PACKED_SIZE_BYTES,
59        ))
60    }
61}
62
63impl FromBytes for Stratum {
64    fn from_bytes(buf: &[u8]) -> Result<(Self, usize), ParseError> {
65        if buf.is_empty() {
66            return Err(ParseError::BufferTooShort {
67                needed: 1,
68                available: 0,
69            });
70        }
71        Ok((Stratum(buf[0]), 1))
72    }
73}
74
75impl FromBytes for (LeapIndicator, Version, Mode) {
76    fn from_bytes(buf: &[u8]) -> Result<(Self, usize), ParseError> {
77        if buf.is_empty() {
78            return Err(ParseError::BufferTooShort {
79                needed: 1,
80                available: 0,
81            });
82        }
83        let li_vn_mode = buf[0];
84        let li_u8 = li_vn_mode >> 6;
85        let vn_u8 = (li_vn_mode >> 3) & 0b111;
86        let mode_u8 = li_vn_mode & 0b111;
87        let li = LeapIndicator::try_from(li_u8).map_err(|_| ParseError::InvalidField {
88            field: "leap indicator",
89            value: li_u8 as u32,
90        })?;
91        let vn = Version(vn_u8);
92        let mode = Mode::try_from(mode_u8).map_err(|_| ParseError::InvalidField {
93            field: "association mode",
94            value: mode_u8 as u32,
95        })?;
96        Ok(((li, vn, mode), 1))
97    }
98}
99
100impl ReferenceIdentifier {
101    /// Parse a reference identifier from 4 bytes, using stratum for disambiguation.
102    ///
103    /// The interpretation of the reference identifier depends on the stratum:
104    /// - Stratum 0: Kiss-o'-Death code
105    /// - Stratum 1: Primary source identifier
106    /// - Stratum 2-15: Secondary/client reference (IPv4 or IPv6 hash)
107    /// - Stratum 16+: Unknown
108    pub fn from_bytes_with_stratum(bytes: [u8; 4], stratum: Stratum) -> Self {
109        let u = u32::from_be_bytes(bytes);
110        if stratum == Stratum::UNSPECIFIED {
111            match KissOfDeath::try_from(u) {
112                Ok(kod) => ReferenceIdentifier::KissOfDeath(kod),
113                Err(_) => ReferenceIdentifier::Unknown(bytes),
114            }
115        } else if stratum == Stratum::PRIMARY {
116            match PrimarySource::try_from(u) {
117                Ok(src) => ReferenceIdentifier::PrimarySource(src),
118                Err(_) => ReferenceIdentifier::Unknown(bytes),
119            }
120        } else if stratum.is_secondary() {
121            ReferenceIdentifier::SecondaryOrClient(bytes)
122        } else {
123            ReferenceIdentifier::Unknown(bytes)
124        }
125    }
126}
127
128impl FromBytes for Packet {
129    fn from_bytes(buf: &[u8]) -> Result<(Self, usize), ParseError> {
130        if buf.len() < Self::PACKED_SIZE_BYTES {
131            return Err(ParseError::BufferTooShort {
132                needed: Self::PACKED_SIZE_BYTES,
133                available: buf.len(),
134            });
135        }
136
137        let mut offset = 0;
138
139        let ((leap_indicator, version, mode), n) =
140            <(LeapIndicator, Version, Mode)>::from_bytes(&buf[offset..])?;
141        offset += n;
142
143        let (stratum, n) = Stratum::from_bytes(&buf[offset..])?;
144        offset += n;
145
146        let poll = buf[offset] as i8;
147        offset += 1;
148
149        let precision = buf[offset] as i8;
150        offset += 1;
151
152        let (root_delay, n) = ShortFormat::from_bytes(&buf[offset..])?;
153        offset += n;
154
155        let (root_dispersion, n) = ShortFormat::from_bytes(&buf[offset..])?;
156        offset += n;
157
158        let ref_id_bytes = [
159            buf[offset],
160            buf[offset + 1],
161            buf[offset + 2],
162            buf[offset + 3],
163        ];
164        let reference_id = ReferenceIdentifier::from_bytes_with_stratum(ref_id_bytes, stratum);
165        offset += 4;
166
167        let (reference_timestamp, n) = TimestampFormat::from_bytes(&buf[offset..])?;
168        offset += n;
169
170        let (origin_timestamp, n) = TimestampFormat::from_bytes(&buf[offset..])?;
171        offset += n;
172
173        let (receive_timestamp, n) = TimestampFormat::from_bytes(&buf[offset..])?;
174        offset += n;
175
176        let (transmit_timestamp, n) = TimestampFormat::from_bytes(&buf[offset..])?;
177        offset += n;
178
179        Ok((
180            Packet {
181                leap_indicator,
182                version,
183                mode,
184                stratum,
185                poll,
186                precision,
187                root_delay,
188                root_dispersion,
189                reference_id,
190                reference_timestamp,
191                origin_timestamp,
192                receive_timestamp,
193                transmit_timestamp,
194            },
195            offset,
196        ))
197    }
198}
199
200// Buffer-based writer implementations (io-independent).
201
202impl ToBytes for ShortFormat {
203    fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, ParseError> {
204        if buf.len() < Self::PACKED_SIZE_BYTES {
205            return Err(ParseError::BufferTooShort {
206                needed: Self::PACKED_SIZE_BYTES,
207                available: buf.len(),
208            });
209        }
210        let s = self.seconds.to_be_bytes();
211        let f = self.fraction.to_be_bytes();
212        buf[0] = s[0];
213        buf[1] = s[1];
214        buf[2] = f[0];
215        buf[3] = f[1];
216        Ok(Self::PACKED_SIZE_BYTES)
217    }
218}
219
220impl ToBytes for TimestampFormat {
221    fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, ParseError> {
222        if buf.len() < Self::PACKED_SIZE_BYTES {
223            return Err(ParseError::BufferTooShort {
224                needed: Self::PACKED_SIZE_BYTES,
225                available: buf.len(),
226            });
227        }
228        let s = self.seconds.to_be_bytes();
229        let f = self.fraction.to_be_bytes();
230        buf[..4].copy_from_slice(&s);
231        buf[4..8].copy_from_slice(&f);
232        Ok(Self::PACKED_SIZE_BYTES)
233    }
234}
235
236impl ToBytes for DateFormat {
237    fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, ParseError> {
238        if buf.len() < Self::PACKED_SIZE_BYTES {
239            return Err(ParseError::BufferTooShort {
240                needed: Self::PACKED_SIZE_BYTES,
241                available: buf.len(),
242            });
243        }
244        buf[..4].copy_from_slice(&self.era_number.to_be_bytes());
245        buf[4..8].copy_from_slice(&self.era_offset.to_be_bytes());
246        buf[8..16].copy_from_slice(&self.fraction.to_be_bytes());
247        Ok(Self::PACKED_SIZE_BYTES)
248    }
249}
250
251impl ToBytes for Stratum {
252    fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, ParseError> {
253        if buf.is_empty() {
254            return Err(ParseError::BufferTooShort {
255                needed: 1,
256                available: 0,
257            });
258        }
259        buf[0] = self.0;
260        Ok(1)
261    }
262}
263
264impl ToBytes for (LeapIndicator, Version, Mode) {
265    fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, ParseError> {
266        if buf.is_empty() {
267            return Err(ParseError::BufferTooShort {
268                needed: 1,
269                available: 0,
270            });
271        }
272        let (li, vn, mode) = *self;
273        let mut li_vn_mode = 0u8;
274        li_vn_mode |= (li as u8) << 6;
275        li_vn_mode |= vn.0 << 3;
276        li_vn_mode |= mode as u8;
277        buf[0] = li_vn_mode;
278        Ok(1)
279    }
280}
281
282impl ToBytes for ReferenceIdentifier {
283    fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, ParseError> {
284        if buf.len() < Self::PACKED_SIZE_BYTES {
285            return Err(ParseError::BufferTooShort {
286                needed: Self::PACKED_SIZE_BYTES,
287                available: buf.len(),
288            });
289        }
290        let bytes = self.as_bytes();
291        buf[..4].copy_from_slice(&bytes);
292        Ok(Self::PACKED_SIZE_BYTES)
293    }
294}
295
296impl ToBytes for Packet {
297    fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, ParseError> {
298        if buf.len() < Self::PACKED_SIZE_BYTES {
299            return Err(ParseError::BufferTooShort {
300                needed: Self::PACKED_SIZE_BYTES,
301                available: buf.len(),
302            });
303        }
304
305        let mut offset = 0;
306
307        let li_vn_mode = (self.leap_indicator, self.version, self.mode);
308        offset += li_vn_mode.to_bytes(&mut buf[offset..])?;
309        offset += self.stratum.to_bytes(&mut buf[offset..])?;
310        buf[offset] = self.poll as u8;
311        offset += 1;
312        buf[offset] = self.precision as u8;
313        offset += 1;
314        offset += self.root_delay.to_bytes(&mut buf[offset..])?;
315        offset += self.root_dispersion.to_bytes(&mut buf[offset..])?;
316        offset += self.reference_id.to_bytes(&mut buf[offset..])?;
317        offset += self.reference_timestamp.to_bytes(&mut buf[offset..])?;
318        offset += self.origin_timestamp.to_bytes(&mut buf[offset..])?;
319        offset += self.receive_timestamp.to_bytes(&mut buf[offset..])?;
320        offset += self.transmit_timestamp.to_bytes(&mut buf[offset..])?;
321
322        Ok(offset)
323    }
324}