broker_ntp/
lib.rs

1/*!
2# Example
3Shows how to use the ntp library to fetch the current time according
4to the requested ntp server.
5
6```rust
7extern crate chrono;
8extern crate broker_ntp;
9
10use chrono::TimeZone;
11
12fn main() {
13    let address = "0.pool.ntp.org:123";
14    let response = broker_ntp::request(address).unwrap();
15    let unix_time = broker_ntp::unix_time::Instant::from(response.transmit_timestamp);
16    let local_time = chrono::Local.timestamp(unix_time.secs(), unix_time.subsec_nanos() as _);
17    println!("{}", local_time);
18}
19```
20*/
21
22#![recursion_limit = "1024"]
23
24#[macro_use]
25extern crate custom_derive;
26#[macro_use]
27extern crate conv;
28#[macro_use]
29extern crate log;
30extern crate byteorder;
31
32use protocol::{ReadBytes, ConstPackedSizeBytes, WriteBytes};
33use std::io;
34use std::net::{ToSocketAddrs, UdpSocket};
35use std::time::Duration;
36
37pub mod protocol;
38pub mod unix_time;
39
40/// Send a blocking request to an ntp server with a hardcoded 5 second timeout.
41///
42///   `addr` can be any valid socket address
43///   returns an error if the server cannot be reached or the response is invalid.
44///
45///   **TODO**: remove hardcoded timeout
46pub fn request<A: ToSocketAddrs>(addr: A) -> io::Result<protocol::Packet> {
47    // Create a packet for requesting from an NTP server as a client.
48    let mut packet = {
49        let leap_indicator = protocol::LeapIndicator::default();
50        let version = protocol::Version::V4;
51        let mode = protocol::Mode::Client;
52        let poll = 0;
53        let precision = 0;
54        let root_delay = protocol::ShortFormat::default();
55        let root_dispersion = protocol::ShortFormat::default();
56        let transmit_timestamp = unix_time::Instant::now().into();
57        let stratum = protocol::Stratum::UNSPECIFIED;
58        let src = protocol::PrimarySource::Null;
59        let reference_id = protocol::ReferenceIdentifier::PrimarySource(src);
60        let reference_timestamp = protocol::TimestampFormat::default();
61        let receive_timestamp = protocol::TimestampFormat::default();
62        let origin_timestamp = protocol::TimestampFormat::default();
63        protocol::Packet {
64            leap_indicator,
65            version,
66            mode,
67            stratum,
68            poll,
69            precision,
70            root_delay,
71            root_dispersion,
72            reference_id,
73            reference_timestamp,
74            origin_timestamp,
75            receive_timestamp,
76            transmit_timestamp,
77        }
78    };
79
80    // Write the packet to a slice of bytes.
81    let mut bytes = [0u8; protocol::Packet::PACKED_SIZE_BYTES];
82    (&mut bytes[..]).write_bytes(&packet)?;
83
84    // Create the socket from which we will send the packet.
85    let sock = UdpSocket::bind("0.0.0.0:0")?;
86    sock.set_read_timeout(Some(Duration::from_secs(5)))?;
87    sock.set_write_timeout(Some(Duration::from_secs(5)))?;
88
89    // Send the data.
90    let sz = sock.send_to(&bytes, addr)?;
91    debug!("{:?}", sock.local_addr());
92    debug!("sent: {}", sz);
93
94    // Receive the response.
95    let res = sock.recv(&mut bytes[..])?;
96    debug!("recv: {:?}", res);
97    debug!("{:?}", &bytes[..]);
98
99    // Read the received packet from the response.
100    packet = (&bytes[..]).read_bytes()?;
101    Ok(packet)
102}
103
104#[test]
105fn test_request_ntp_org() {
106    let res = request("0.pool.ntp.org:123");
107    let _ = res.expect("Failed to get a ntp packet from ntp.org");
108}
109
110#[test]
111fn test_request_google() {
112    let res = request("time.google.com:123");
113    let _ = res.expect("Failed to get a ntp packet from time.google.com");
114}