mod error;
use crate::error::ClockBoundCError;
use byteorder::{ByteOrder, NetworkEndian};
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::os::unix::net::UnixDatagram;
use std::path::PathBuf;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
pub const CLOCKBOUNDD_SOCKET_ADDRESS_PATH: &str = "/run/clockboundd/clockboundd.sock";
pub const CLOCKBOUNDC_SOCKET_NAME_PREFIX: &str = "clockboundc";
pub const FREQUENCY_ERROR: u64 = 1;
pub struct Bound {
pub earliest: u64,
pub latest: u64,
}
pub struct ResponseHeader {
pub response_version: u8,
pub response_type: u8,
pub unsynchronized_flag: bool,
}
pub struct ResponseNow {
pub header: ResponseHeader,
pub bound: Bound,
pub timestamp: u64,
}
pub struct ResponseBefore {
pub header: ResponseHeader,
pub before: bool,
}
pub struct ResponseAfter {
pub header: ResponseHeader,
pub after: bool,
}
#[derive(Debug)]
pub struct TimingResult {
pub earliest_start : SystemTime,
pub latest_finish : SystemTime,
pub min_execution_time : Duration,
pub max_execution_time : Duration
}
pub struct ClockBoundClient {
socket: UnixDatagram,
}
impl ClockBoundClient {
pub fn new() -> Result<ClockBoundClient, ClockBoundCError> {
ClockBoundClient::new_with_path(std::path::PathBuf::from(CLOCKBOUNDD_SOCKET_ADDRESS_PATH))
}
pub fn new_with_path(
clock_bound_d_socket: PathBuf,
) -> Result<ClockBoundClient, ClockBoundCError> {
let client_path = get_socket_path();
let sock = match UnixDatagram::bind(client_path.as_path()) {
Ok(sock) => sock,
Err(e) => return Err(ClockBoundCError::BindError(e)),
};
let mode = 0o666;
let permissions = fs::Permissions::from_mode(mode);
match fs::set_permissions(client_path, permissions) {
Err(e) => return Err(ClockBoundCError::SetPermissionsError(e)),
_ => {}
}
match sock.connect(clock_bound_d_socket.as_path()) {
Err(e) => return Err(ClockBoundCError::ConnectError(e)),
_ => {}
}
Ok(ClockBoundClient { socket: sock })
}
pub fn now(&self) -> Result<ResponseNow, ClockBoundCError> {
let mut request: [u8; 4] = [1, 1, 0, 0];
match self.socket.send(&mut request) {
Err(e) => return Err(ClockBoundCError::SendMessageError(e)),
_ => {}
}
let mut response: [u8; 20] = [0; 20];
match self.socket.recv(&mut response) {
Err(e) => return Err(ClockBoundCError::ReceiveMessageError(e)),
_ => {}
}
let response_version = response[0];
let response_type = response[1];
let unsynchronized_flag = response[2] != 0;
let earliest = NetworkEndian::read_u64(&response[4..12]);
let latest = NetworkEndian::read_u64(&response[12..20]);
Ok(ResponseNow {
header: ResponseHeader {
response_version,
response_type,
unsynchronized_flag,
},
bound: Bound { earliest, latest },
timestamp: (latest - ((latest - earliest) / 2)),
})
}
pub fn before(&self, before_time: u64) -> Result<ResponseBefore, ClockBoundCError> {
let mut request: [u8; 12] = [1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
NetworkEndian::write_u64(&mut request[4..12], before_time);
match self.socket.send(&mut request) {
Err(e) => return Err(ClockBoundCError::SendMessageError(e)),
_ => {}
}
let mut response: [u8; 5] = [0; 5];
match self.socket.recv(&mut response) {
Err(e) => return Err(ClockBoundCError::ReceiveMessageError(e)),
_ => {}
}
let response_version = response[0];
let response_type = response[1];
let unsynchronized_flag = response[2] != 0;
let before = response[4] != 0;
Ok(ResponseBefore {
header: ResponseHeader {
response_version,
response_type,
unsynchronized_flag,
},
before,
})
}
pub fn after(&self, after_time: u64) -> Result<ResponseAfter, ClockBoundCError> {
let mut request: [u8; 12] = [1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
NetworkEndian::write_u64(&mut request[4..12], after_time);
match self.socket.send(&mut request) {
Err(e) => return Err(ClockBoundCError::SendMessageError(e)),
_ => {}
}
let mut response: [u8; 5] = [0; 5];
match self.socket.recv(&mut response) {
Err(e) => return Err(ClockBoundCError::ReceiveMessageError(e)),
_ => {}
}
let response_version = response[0];
let response_type = response[1];
let unsynchronized_flag = response[2] != 0;
let after = response[4] != 0;
Ok(ResponseAfter {
header: ResponseHeader {
response_version,
response_type,
unsynchronized_flag,
},
after,
})
}
pub fn timing<A, F>(&self, f: F) -> Result<(TimingResult, A), (ClockBoundCError, Result<A,F>)>
where F: FnOnce() -> A {
let mut request: [u8; 4] = [1, 1, 0, 0];
match self.socket.send(&mut request) {
Err(e) => return Err((ClockBoundCError::SendMessageError(e), Err(f))),
_ => {}
}
let mut response: [u8; 20] = [0; 20];
match self.socket.recv(&mut response) {
Err(e) => return Err((ClockBoundCError::ReceiveMessageError(e), Err(f))),
_ => {}
}
let earliest_start = NetworkEndian::read_u64(&response[4..12]);
let latest_start = NetworkEndian::read_u64(&response[12..20]);
let callback = f();
let mut request: [u8; 4] = [1, 1, 0, 0];
match self.socket.send(&mut request) {
Err(e) => return Err((ClockBoundCError::SendMessageError(e), Ok(callback))),
_ => {}
}
let mut response: [u8; 20] = [0; 20];
match self.socket.recv(&mut response) {
Err(e) => return Err((ClockBoundCError::ReceiveMessageError(e), Ok(callback))),
_ => {}
}
let earliest_finish = NetworkEndian::read_u64(&response[4..12]);
let latest_finish = NetworkEndian::read_u64(&response[12..20]);
let start_midpoint = (earliest_start + latest_start)/2;
let end_midpoint = (earliest_finish + latest_finish)/2;
let earliest_start = UNIX_EPOCH + Duration::from_nanos(earliest_start);
let latest_finish = UNIX_EPOCH + Duration::from_nanos(latest_finish);
let execution_time = end_midpoint - start_midpoint;
let error_rate = (execution_time * FREQUENCY_ERROR) / 1_000_000 +
if (execution_time * FREQUENCY_ERROR) % 1_000_000 == 0 { 0 } else { 1 };
let min_execution_time = Duration::from_nanos(execution_time - error_rate);
let max_execution_time = Duration::from_nanos(execution_time + error_rate);
Ok((TimingResult{
earliest_start,
latest_finish,
min_execution_time,
max_execution_time
}, callback))
}
}
impl Drop for ClockBoundClient {
fn drop(&mut self) {
if let Ok(addr) = self.socket.local_addr() {
if let Some(path) = addr.as_pathname() {
let _ = self.socket.shutdown(std::net::Shutdown::Both);
let _ = std::fs::remove_file(path);
}
}
}
}
fn get_socket_path() -> PathBuf {
let dir = std::env::temp_dir();
let mut rng = thread_rng();
let random_str: String = (&mut rng)
.sample_iter(Alphanumeric)
.take(20)
.map(char::from)
.collect();
let client_path_buf =
dir.join(CLOCKBOUNDC_SOCKET_NAME_PREFIX.to_owned() + "-" + &*random_str + ".sock");
return client_path_buf;
}