chrony_candm/
common.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Portions derived from Chrony copyright Richard P. Curnow 1997-2003
3// and Miroslav Lichvar 2009, 2012-2020
4// SPDX-License-Identifier: GPL-2.0-only
5
6//! Data structures occurring both in requests and in replies
7
8use bitflags::bitflags;
9use bytes::{Buf, BufMut};
10use std::convert::From;
11use std::error::Error;
12use std::fmt::{Debug, Display};
13use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
14use std::time::{Duration, SystemTime, UNIX_EPOCH};
15
16use chrony_candm_derive::ChronySerialize;
17
18#[derive(Debug, thiserror::Error)]
19pub enum QueryError {
20    #[error("Unable to send request")]
21    Send(#[source] std::io::Error),
22    #[error("Unable to receive reply")]
23    Recv(#[source] std::io::Error),
24    #[error("Failed deserialization")]
25    Deserialization(#[from] DeserializationError),
26    #[error("Sequence number mismatch. Expected {expected}, got {received}")]
27    SequenceMismatch { expected: u32, received: u32 },
28    #[error("Timeout")]
29    Timeout,
30}
31
32impl QueryError {
33    // Convert to IO error for backwards compatibility
34    pub(crate) fn into_io(self) -> std::io::Error {
35        match self {
36            QueryError::Send(e) => e,
37            QueryError::Recv(e) => e,
38            QueryError::Deserialization(e) => std::io::Error::new(std::io::ErrorKind::InvalidData, e),
39            QueryError::SequenceMismatch { .. } => std::io::Error::new(
40                std::io::ErrorKind::InvalidData,
41                String::from("Sequence number mismatch"),
42            ),
43            QueryError::Timeout => std::io::Error::new(std::io::ErrorKind::TimedOut, "Timed out"),
44        }
45    }
46}
47
48///Error returned when attempting to deserialize a malformed message
49#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
50pub struct DeserializationError(Option<&'static str>);
51
52impl DeserializationError {
53    pub(crate) fn new(detail: &'static str) -> DeserializationError {
54        DeserializationError(Some(detail))
55    }
56
57    #[allow(dead_code)]
58    pub(crate) fn generic() -> DeserializationError {
59        DeserializationError(None)
60    }
61}
62
63impl Display for DeserializationError {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        match self.0 {
66            None => write!(f, "Deserialization error"),
67            Some(detail) => write!(f, "Deserialization error: {}", detail),
68        }
69    }
70}
71impl Error for DeserializationError {}
72
73pub(crate) trait ChronySerialize: Sized {
74    fn length() -> usize;
75    fn serialize<B: BufMut>(&self, buf: &mut B);
76    fn deserialize_unchecked<B: Buf>(buf: &mut B) -> Result<Self, DeserializationError>;
77
78    fn deserialize<B: Buf>(buf: &mut B) -> Result<Self, DeserializationError> {
79        if buf.remaining() < Self::length() {
80            Err(DeserializationError::new("message too short"))
81        } else {
82            Self::deserialize_unchecked(buf)
83        }
84    }
85}
86
87impl<T: ChronySerialize + Copy, const N: usize> ChronySerialize for [T; N] {
88    fn length() -> usize {
89        T::length() * N
90    }
91
92    fn serialize<B: BufMut>(&self, buf: &mut B) {
93        for i in 0..N {
94            self[i].serialize(buf)
95        }
96    }
97
98    fn deserialize_unchecked<B: Buf>(buf: &mut B) -> Result<Self, DeserializationError> {
99        let mut arr = arrayvec::ArrayVec::<T, N>::new_const();
100        unsafe {
101            for _ in 0..N {
102                //Safety: We push at most N elements
103                arr.push_unchecked(T::deserialize_unchecked(buf)?);
104            }
105            //Safety: we've pushed exactly N elements
106            Ok(arr.into_inner_unchecked())
107        }
108    }
109}
110
111macro_rules! serialize_primitive {
112    ($ty:ty, $get:ident, $put:ident) => {
113        impl ChronySerialize for $ty {
114            fn length() -> usize {
115                std::mem::size_of::<$ty>()
116            }
117
118            fn serialize<B: BufMut>(&self, buf: &mut B) {
119                buf.$put(*self)
120            }
121
122            fn deserialize_unchecked<B: Buf>(buf: &mut B) -> Result<Self, DeserializationError> {
123                Ok(buf.$get())
124            }
125        }
126    };
127}
128
129serialize_primitive!(u8, get_u8, put_u8);
130serialize_primitive!(i8, get_i8, put_i8);
131serialize_primitive!(u16, get_u16, put_u16);
132serialize_primitive!(i16, get_i16, put_i16);
133serialize_primitive!(u32, get_u32, put_u32);
134serialize_primitive!(i32, get_i32, put_i32);
135serialize_primitive!(u64, get_u64, put_u64);
136serialize_primitive!(i64, get_i64, put_i64);
137
138impl ChronySerialize for SystemTime {
139    fn length() -> usize {
140        12
141    }
142
143    fn serialize<B: BufMut>(&self, buf: &mut B) {
144        let (secs, nsecs) = match self.duration_since(UNIX_EPOCH) {
145            Ok(d) => (d.as_secs() as i64, d.subsec_nanos()),
146            Err(e) => {
147                let d = e.duration();
148                let secs = d.as_secs() as i64;
149                let nsecs = d.subsec_nanos();
150                if nsecs == 0 {
151                    (-secs, 0)
152                } else {
153                    (-secs - 1, 1_000_000_000 - nsecs)
154                }
155            }
156        };
157
158        let secs_high = (secs >> 32) as i32;
159        let secs_low = (secs & 0xffffffff) as u32;
160
161        buf.put_i32(secs_high);
162        buf.put_u32(secs_low);
163        buf.put_u32(nsecs);
164    }
165
166    fn deserialize_unchecked<B: Buf>(buf: &mut B) -> Result<Self, DeserializationError> {
167        let secs_high_orig = buf.get_i32();
168        let secs_low = buf.get_u32();
169        let nsecs = buf.get_u32();
170
171        let secs_high = if secs_high_orig == i32::MAX {
172            0
173        } else {
174            secs_high_orig
175        };
176
177        if secs_high >= 0 {
178            let d = Duration::from_secs((secs_high as u64) << 32)
179                + Duration::from_secs(secs_low as u64)
180                + Duration::from_nanos(nsecs as u64);
181            Ok(UNIX_EPOCH + d)
182        } else {
183            let d = Duration::from_secs(((-secs_high) as u64) << 32)
184                - Duration::from_secs(secs_low as u64)
185                - Duration::from_nanos(nsecs as u64);
186            Ok(UNIX_EPOCH - d)
187        }
188    }
189}
190pub(crate) trait ChronyMessage: Sized {
191    fn body_length(&self) -> usize;
192    fn cmd(&self) -> u16;
193    fn serialize_body<B: BufMut>(&self, buf: &mut B);
194    fn deserialize_body<B: Buf>(cmd: u16, body: &mut B) -> Result<Self, DeserializationError>;
195}
196
197/// Floating point number as used in Chrony's wire protocol
198///
199/// A `ChronyFloat` can be converted infallibly to and from an `f64`,
200/// but the conversion into `ChronyFloat` is lossy and sometimes
201/// nonsensical, e.g., NaNs are represented as 0. These conversion
202/// rules are identical to the ones that Chrony uses internally.
203#[derive(Copy, Clone, Eq, PartialEq, Hash, Default)]
204pub struct ChronyFloat(u32);
205
206impl Debug for ChronyFloat {
207    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208        f.debug_tuple("ChronyFloat")
209            .field(&f64::from(*self))
210            .finish()
211    }
212}
213
214impl Display for ChronyFloat {
215    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216        Display::fmt(&f64::from(*self), f)
217    }
218}
219
220impl PartialOrd for ChronyFloat {
221    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
222        f64::from(*self).partial_cmp(&f64::from(*other))
223    }
224}
225
226impl Ord for ChronyFloat {
227    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
228        self.partial_cmp(other)
229            .expect("Conversion from ChronyFloat yielded incomparable f64")
230    }
231}
232
233const FLOAT_EXP_BITS: u32 = 7;
234const FLOAT_EXP_MIN: i32 = -((1 << (FLOAT_EXP_BITS - 1)) as i32);
235const FLOAT_EXP_MAX: i32 = -FLOAT_EXP_MIN - 1;
236const FLOAT_COEF_BITS: u32 = 32 - FLOAT_EXP_BITS;
237const FLOAT_COEF_MIN: i32 = -((1 << (FLOAT_COEF_BITS - 1)) as i32);
238const FLOAT_COEF_MAX: i32 = -FLOAT_COEF_MIN - 1;
239
240impl From<ChronyFloat> for f64 {
241    fn from(f: ChronyFloat) -> Self {
242        let ChronyFloat(x) = f;
243
244        let mut exp = (x >> FLOAT_COEF_BITS) as i32;
245        if exp >= 1 << (FLOAT_EXP_BITS - 1) {
246            exp -= 1 << FLOAT_EXP_BITS;
247        }
248        exp -= FLOAT_COEF_BITS as i32;
249
250        let mut coef = (x % (1 << FLOAT_COEF_BITS)) as i32;
251
252        if coef >= 1 << (FLOAT_COEF_BITS - 1) {
253            coef -= 1 << FLOAT_COEF_BITS
254        }
255
256        (coef as f64) * 2.0f64.powi(exp)
257    }
258}
259
260impl From<f64> for ChronyFloat {
261    fn from(f: f64) -> Self {
262        let (neg, x) = if f < 0. {
263            (1, -f)
264        } else if f >= 0. {
265            (0, f)
266        } else {
267            (0, 0.) //Treat NaN as zero
268        };
269
270        let mut exp: i32;
271        let mut coef: i32;
272
273        if x < 1e-100 {
274            coef = 0;
275            exp = 0;
276        } else if x > 1e100 {
277            coef = FLOAT_COEF_MAX + neg;
278            exp = FLOAT_EXP_MAX;
279        } else {
280            exp = x.log2() as i32 + 1;
281            coef = (x * 2.0f64.powi(-exp + FLOAT_COEF_BITS as i32) + 0.5) as i32;
282            debug_assert!(coef > 0);
283
284            while coef > FLOAT_COEF_MAX + neg {
285                coef >>= 1;
286                exp += 1;
287            }
288
289            if exp > FLOAT_EXP_MAX {
290                exp = FLOAT_EXP_MAX;
291                coef = FLOAT_COEF_MAX + neg;
292            } else if exp < FLOAT_EXP_MIN {
293                if exp + FLOAT_COEF_BITS as i32 >= FLOAT_EXP_MIN {
294                    coef >>= FLOAT_EXP_MIN - exp;
295                    exp = FLOAT_EXP_MIN;
296                } else {
297                    exp = 0;
298                    coef = 0;
299                }
300            }
301        };
302
303        if neg != 0 {
304            coef = ((-coef as u32) << FLOAT_EXP_BITS >> FLOAT_EXP_BITS) as i32;
305        }
306
307        ChronyFloat((exp as u32) << FLOAT_COEF_BITS | coef as u32)
308    }
309}
310
311impl ChronySerialize for ChronyFloat {
312    fn length() -> usize {
313        4
314    }
315
316    fn serialize<B: BufMut>(&self, buf: &mut B) {
317        self.0.serialize(buf)
318    }
319
320    fn deserialize_unchecked<B: Buf>(buf: &mut B) -> Result<Self, DeserializationError> {
321        Ok(ChronyFloat(u32::deserialize_unchecked(buf)?))
322    }
323}
324
325/// A network address as used in Chrony's wire protocol
326#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
327pub enum ChronyAddr {
328    /// An unknown or unspecified address
329    Unspec,
330    /// An IPv4 address
331    V4(Ipv4Addr),
332    /// An IPv6 address
333    V6(Ipv6Addr),
334    /// A placeholder for an address that has not yet been resolved
335    Id(u32),
336}
337
338impl Default for ChronyAddr {
339    fn default() -> Self {
340        Self::Unspec
341    }
342}
343
344impl From<Ipv4Addr> for ChronyAddr {
345    fn from(addr: Ipv4Addr) -> Self {
346        Self::V4(addr)
347    }
348}
349
350impl From<Ipv6Addr> for ChronyAddr {
351    fn from(addr: Ipv6Addr) -> Self {
352        Self::V6(addr)
353    }
354}
355
356impl From<IpAddr> for ChronyAddr {
357    fn from(addr: IpAddr) -> Self {
358        match addr {
359            IpAddr::V4(addr4) => Self::V4(addr4),
360            IpAddr::V6(addr6) => Self::V6(addr6),
361        }
362    }
363}
364
365impl Display for ChronyAddr {
366    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
367        match self {
368            ChronyAddr::Unspec => write!(f, "[UNSPEC]"),
369            ChronyAddr::V4(v4) => Display::fmt(v4, f),
370            ChronyAddr::V6(v6) => Display::fmt(v6, f),
371            ChronyAddr::Id(id) => write!(f, "ID#{}", id),
372        }
373    }
374}
375
376const IPADDR_UNSPEC: u16 = 0;
377const IPADDR_INET4: u16 = 1;
378const IPADDR_INET6: u16 = 2;
379const IPADDR_ID: u16 = 3;
380
381impl ChronySerialize for ChronyAddr {
382    fn length() -> usize {
383        20
384    }
385
386    fn serialize<B: BufMut>(&self, buf: &mut B) {
387        match self {
388            ChronyAddr::Unspec => {
389                buf.put(&[0u8; 16] as &[u8]);
390                buf.put_u16(IPADDR_UNSPEC);
391                buf.put_u16(0);
392            }
393            ChronyAddr::V4(addr) => {
394                buf.put_u32((*addr).into());
395                buf.put(&[0u8; 12] as &[u8]);
396                buf.put_u16(IPADDR_INET4);
397                buf.put_u16(0);
398            }
399            ChronyAddr::V6(addr) => {
400                buf.put_u128((*addr).into());
401                buf.put_u16(IPADDR_INET6);
402                buf.put_u16(0);
403            }
404            ChronyAddr::Id(id) => {
405                buf.put_u32(*id);
406                buf.put(&[0u8; 12] as &[u8]);
407                buf.put_u16(IPADDR_ID);
408                buf.put_u16(0);
409            }
410        }
411    }
412
413    fn deserialize_unchecked<B: Buf>(buf: &mut B) -> Result<Self, DeserializationError> {
414        let mut addr = buf.copy_to_bytes(16);
415        let family = buf.get_u16();
416        buf.get_u16();
417
418        match family {
419            IPADDR_INET4 => Ok(Self::V4(Ipv4Addr::from(addr.get_u32()))),
420            IPADDR_INET6 => Ok(Self::V6(Ipv6Addr::from(addr.get_u128()))),
421            IPADDR_ID => Ok(Self::Id(addr.get_u32())),
422            _ => Ok(Self::Unspec),
423        }
424    }
425}
426
427bitflags! {
428    /// Flags associated with a time source
429    #[derive(ChronySerialize)]
430    pub struct SourceFlags : u16 {
431        const ONLINE = 0x1;
432        const AUTOOFFLINE = 0x2;
433        const IBURST = 0x4;
434        const PREFER = 0x8;
435        const NOSELECT = 0x10;
436        const TRUST = 0x20;
437        const REQUIRE = 0x40;
438        const INTERLEAVED = 0x80;
439        const BURST = 0x100;
440        const NTS = 0x200;
441        const COPY = 0x400;
442    }
443}
444
445pub(crate) const REQUEST_HEADER_LENGTH: usize = 20;
446pub(crate) const REPLY_HEADER_LENGTH: usize = 28;