#![cfg_attr(
feature = "async",
doc = r##"
A function which uses the asynchronous API to obtain local time:
```no_run
use rsntp::AsyncSntpClient;
use chrono::{DateTime, Local};
async fn local_time() -> DateTime<Local> {
let client = AsyncSntpClient::new();
let result = client.synchronize("pool.ntp.org").await.unwrap();
DateTime::from(result.datetime())
}
```
## Disabling asynchronous API
The asynchronous API is enabled by default but you can optionally disable it. This removes
dependency to `tokio` which reduces crate dependencies significantly.
```toml
[dependencies]
rsntp = { version = "2.0.0", default-features = false }
```
"##
)]
mod core_logic;
mod error;
mod packet;
mod to_server_addrs;
pub use core_logic::SynchronizationResult;
pub use error::{KissCode, ProtocolError, SynchroniztationError};
pub use packet::{LeapIndicator, ReferenceIdentifier};
pub use to_server_addrs::ToServerAddrs;
use core_logic::{Reply, Request};
use packet::Packet;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::time::Duration;
#[cfg(feature = "async")]
use tokio::time::timeout;
const SNTP_PORT: u16 = 123;
#[derive(Clone, Debug, Hash)]
pub struct SntpClient {
bind_address: SocketAddr,
timeout: Duration,
}
impl SntpClient {
pub fn new() -> SntpClient {
SntpClient {
bind_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0),
timeout: Duration::from_secs(3),
}
}
pub fn synchronize<A: ToServerAddrs>(
&self,
server_address: A,
) -> Result<SynchronizationResult, SynchroniztationError> {
let socket = std::net::UdpSocket::bind(self.bind_address)?;
socket.set_read_timeout(Some(self.timeout))?;
socket.connect(server_address.to_server_addrs(SNTP_PORT))?;
let request = Request::new();
let mut receive_buffer = [0; Packet::ENCODED_LEN];
socket.send(&request.as_bytes())?;
let (bytes_received, server_address) = socket.recv_from(&mut receive_buffer)?;
let reply = Reply::new(
request,
Packet::from_bytes(&receive_buffer[..bytes_received], server_address)?,
);
reply.process()
}
pub fn set_timeout(&mut self, timeout: Duration) {
self.timeout = timeout;
}
pub fn set_bind_address(&mut self, address: SocketAddr) {
self.bind_address = address;
}
}
impl Default for SntpClient {
fn default() -> Self {
SntpClient::new()
}
}
#[cfg(feature = "async")]
pub struct AsyncSntpClient {
bind_address: SocketAddr,
timeout: Duration,
}
#[cfg(feature = "async")]
impl AsyncSntpClient {
pub fn new() -> AsyncSntpClient {
AsyncSntpClient {
bind_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0),
timeout: Duration::from_secs(3),
}
}
pub async fn synchronize<A: ToServerAddrs>(
&self,
server_address: A,
) -> Result<SynchronizationResult, SynchroniztationError> {
let mut receive_buffer = [0; Packet::ENCODED_LEN];
let socket = tokio::net::UdpSocket::bind(self.bind_address).await?;
socket
.connect(server_address.to_server_addrs(SNTP_PORT))
.await?;
let request = Request::new();
socket.send(&request.as_bytes()).await?;
let result_future = timeout(self.timeout, socket.recv_from(&mut receive_buffer));
let (bytes_received, server_address) = result_future.await.map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Timeout while waiting for server reply",
)
})??;
let reply = Reply::new(
request,
Packet::from_bytes(&receive_buffer[..bytes_received], server_address)?,
);
reply.process()
}
pub fn set_timeout(&mut self, timeout: Duration) {
self.timeout = timeout;
}
pub fn set_bind_address(&mut self, address: SocketAddr) {
self.bind_address = address;
}
}
#[cfg(feature = "async")]
impl Default for AsyncSntpClient {
fn default() -> Self {
AsyncSntpClient::new()
}
}