1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! This crate contains networking and timestamping code for ntpd-rs and is not
//! intended as a public interface at this time. It follows the same version as the
//! main ntpd-rs crate, but that version is not intended to give any stability
//! guarantee. Use at your own risk.
//!
//! Please visit the [ntpd-rs](https://github.com/pendulum-project/ntpd-rs) project
//! for more information.
#![forbid(unsafe_op_in_unsafe_fn)]

mod interface_name;
mod raw_socket;
mod socket;

#[cfg(target_os = "linux")]
mod hwtimestamp;

use std::{ops::Deref, str::FromStr};

use ntp_proto::NtpTimestamp;

use serde::Deserialize;
pub use socket::UdpSocket;

/// Enable the given timestamps. This is a hint!
///
/// Your OS or hardware might not actually support some timestamping modes.
/// Unsupported timestamping modes are ignored.
#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct EnableTimestamps {
    #[serde(default = "bool_true")]
    pub rx_software: bool,
    #[serde(default)] // defaults to `false`
    pub tx_software: bool,
    #[serde(default)] // defaults to `false`
    pub rx_hardware: bool,
    #[serde(default)] // defaults to `false`
    pub tx_hardware: bool,
}

impl Default for EnableTimestamps {
    fn default() -> Self {
        Self {
            rx_software: true,
            tx_software: false,
            rx_hardware: false,
            tx_hardware: false,
        }
    }
}

#[derive(Clone, Copy)]
pub(crate) enum LibcTimestamp {
    #[cfg_attr(any(target_os = "macos", target_os = "freebsd"), allow(unused))]
    Timespec(libc::timespec),
    Timeval(libc::timeval),
}

impl LibcTimestamp {
    pub(crate) fn into_ntp_timestamp(self) -> NtpTimestamp {
        // Unix uses an epoch located at 1/1/1970-00:00h (UTC) and NTP uses 1/1/1900-00:00h.
        // This leads to an offset equivalent to 70 years in seconds
        // there are 17 leap years between the two dates so the offset is
        const EPOCH_OFFSET: u32 = (70 * 365 + 17) * 86400;

        match self {
            LibcTimestamp::Timespec(timespec) => {
                // truncates the higher bits of the i64
                let seconds = (timespec.tv_sec as u32).wrapping_add(EPOCH_OFFSET);

                // tv_nsec is always within [0, 1e10)
                let nanos = timespec.tv_nsec as u32;

                NtpTimestamp::from_seconds_nanos_since_ntp_era(seconds, nanos)
            }
            LibcTimestamp::Timeval(timeval) => {
                // truncates the higher bits of the i64
                let seconds = (timeval.tv_sec as u32).wrapping_add(EPOCH_OFFSET);

                let micros = timeval.tv_usec as u32;
                let nanos = micros * 1000;

                NtpTimestamp::from_seconds_nanos_since_ntp_era(seconds, nanos)
            }
        }
    }
}

fn bool_true() -> bool {
    true
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub struct InterfaceName {
    bytes: [u8; libc::IFNAMSIZ],
}

impl Deref for InterfaceName {
    type Target = [u8];

    fn deref(&self) -> &Self::Target {
        self.bytes.as_slice()
    }
}

impl<'de> Deserialize<'de> for InterfaceName {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        use InterfaceNameParseError::*;

        let name: String = Deserialize::deserialize(deserializer)?;

        match Self::from_str(&name) {
            Ok(v) => Ok(v),
            Err(Empty) => Err(serde::de::Error::custom("interface name empty")),
            Err(TooLong) => Err(serde::de::Error::custom("interface name too long")),
        }
    }
}

#[derive(Debug)]
pub enum InterfaceNameParseError {
    Empty,
    TooLong,
}

impl FromStr for InterfaceName {
    type Err = InterfaceNameParseError;

    fn from_str(name: &str) -> Result<Self, Self::Err> {
        if name.is_empty() {
            return Err(InterfaceNameParseError::Empty);
        }

        let mut it = name.bytes();
        let bytes = std::array::from_fn(|_| it.next().unwrap_or_default());

        if it.next().is_some() {
            Err(InterfaceNameParseError::TooLong)
        } else {
            Ok(InterfaceName { bytes })
        }
    }
}

impl InterfaceName {
    pub const DEFAULT: Option<Self> = None;

    fn as_str(&self) -> &str {
        std::str::from_utf8(self.bytes.as_slice())
            .unwrap_or_default()
            .trim_end_matches('\0')
    }
}

impl std::fmt::Debug for InterfaceName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("InterfaceName")
            .field(&self.as_str())
            .finish()
    }
}

impl std::fmt::Display for InterfaceName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.as_str().fmt(f)
    }
}