#![recursion_limit = "1024"]
#[macro_use]
extern crate custom_derive;
extern crate conv;
#[macro_use]
extern crate log;
extern crate byteorder;
pub mod protocol;
use protocol::{ReadBytes, ConstPackedSizeBytes, WriteBytes};
use std::io;
#[cfg(feature = "async-std")]
use async_std::net::{ToSocketAddrs, UdpSocket};
#[cfg(feature = "tokio")]
use tokio::net::{ToSocketAddrs, UdpSocket};
use anyhow::Result;
use std::{self, time};
pub async fn request<A: ToSocketAddrs>(addr: A) -> io::Result<protocol::Packet> {
let mut packet = {
let leap_indicator = protocol::LeapIndicator::default();
let version = protocol::Version::V4;
let mode = protocol::Mode::Client;
let poll = 0;
let precision = 0;
let root_delay = protocol::ShortFormat::default();
let root_dispersion = protocol::ShortFormat::default();
let transmit_timestamp = Instant::now().into();
let stratum = protocol::Stratum::UNSPECIFIED;
let src = protocol::PrimarySource::Null;
let reference_id = protocol::ReferenceIdentifier::PrimarySource(src);
let reference_timestamp = protocol::TimestampFormat::default();
let receive_timestamp = protocol::TimestampFormat::default();
let origin_timestamp = protocol::TimestampFormat::default();
protocol::Packet {
leap_indicator,
version,
mode,
stratum,
poll,
precision,
root_delay,
root_dispersion,
reference_id,
reference_timestamp,
origin_timestamp,
receive_timestamp,
transmit_timestamp,
}
};
let mut bytes = [0u8; protocol::Packet::PACKED_SIZE_BYTES];
(&mut bytes[..]).write_bytes(&packet)?;
let sock = UdpSocket::bind("0.0.0.0:0").await?;
let sz = sock.send_to(&bytes, addr).await?;
debug!("{:?}", sock.local_addr());
debug!("sent: {}", sz);
let res = sock.recv(&mut bytes[..]).await?;
debug!("recv: {:?}", res);
debug!("{:?}", &bytes[..]);
packet = (&bytes[..]).read_bytes()?;
Ok(packet)
}
pub const EPOCH_DELTA: i64 = 2_208_988_800;
const NTP_SCALE: f64 = std::u32::MAX as f64;
#[derive(Copy, Clone, Debug)]
pub struct Instant {
secs: i64,
subsec_nanos: i32,
}
impl Instant {
pub fn new(secs: i64, subsec_nanos: i32) -> Instant {
if secs > 0 && subsec_nanos < 0 {
panic!("invalid instant: secs was positive but subsec_nanos was negative");
}
if secs < 0 && subsec_nanos > 0 {
panic!("invalid instant: secs was negative but subsec_nanos was positive");
}
Instant { secs, subsec_nanos }
}
pub fn now() -> Self {
match time::SystemTime::now().duration_since(time::UNIX_EPOCH) {
Ok(duration) => {
let secs = duration.as_secs() as i64;
let subsec_nanos = duration.subsec_nanos() as i32;
Instant::new(secs, subsec_nanos)
}
Err(sys_time_err) => {
let duration_pre_unix_epoch = sys_time_err.duration();
let secs = -(duration_pre_unix_epoch.as_secs() as i64);
let subsec_nanos = -(duration_pre_unix_epoch.subsec_nanos() as i32);
Instant::new(secs, subsec_nanos)
}
}
}
pub fn secs(&self) -> i64 {
self.secs
}
pub fn subsec_nanos(&self) -> i32 {
self.subsec_nanos
}
}
impl From<protocol::ShortFormat> for Instant {
fn from(t: protocol::ShortFormat) -> Self {
let secs = t.seconds as i64 - EPOCH_DELTA;
let subsec_nanos = (t.fraction as f64 / NTP_SCALE * 1e9) as i32;
Instant::new(secs, subsec_nanos)
}
}
impl From<protocol::TimestampFormat> for Instant {
fn from(t: protocol::TimestampFormat) -> Self {
let secs = t.seconds as i64 - EPOCH_DELTA;
let subsec_nanos = (t.fraction as f64 / NTP_SCALE * 1e9) as i32;
Instant::new(secs, subsec_nanos)
}
}
impl From<Instant> for protocol::ShortFormat {
fn from(t: Instant) -> Self {
let sec = t.secs() + EPOCH_DELTA;
let frac = t.subsec_nanos() as f64 * NTP_SCALE / 1e10;
protocol::ShortFormat {
seconds: sec as u16,
fraction: frac as u16,
}
}
}
impl From<Instant> for protocol::TimestampFormat {
fn from(t: Instant) -> Self {
let sec = t.secs() + EPOCH_DELTA;
let frac = t.subsec_nanos() as f64 * NTP_SCALE / 1e10;
protocol::TimestampFormat {
seconds: sec as u32,
fraction: frac as u32,
}
}
}
pub async fn get_unix_ntp_time() -> Result<i64> {
let pool_ntp = "pool.ntp.org:123";
let response = request(pool_ntp).await?;
let timestamp = response.transmit_timestamp;
Ok(Instant::from(timestamp).secs())
}