// Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![allow(clippy::upper_case_acronyms)] use crate::nss_prelude::PRInt64; use crate::err::{Error, Res}; //use crate::prio::PRFileDesc; //use crate::ssl::SSLTimeFunc; use once_cell::sync::OnceCell; //use std::boxed::Box; use std::convert::{TryFrom, TryInto}; use std::ops::Deref; //use std::os::raw::c_void; //use std::pin::Pin; use std::time::{Duration, Instant}; include!(concat!(env!("OUT_DIR"), "/nspr_time.rs")); /* TODO move to exp module experimental_api!(SSL_SetTimeFunc( fd: *mut PRFileDesc, cb: SSLTimeFunc, arg: *mut c_void, )); */ /// This struct holds the zero time used for converting between `Instant` and `PRTime`. #[derive(Debug)] struct TimeZero { instant: Instant, prtime: PRTime, } impl TimeZero { /// This function sets a baseline from an instance of `Instant`. /// This allows for the possibility that code that uses these APIs will create /// instances of `Instant` before any of this code is run. If `Instant`s older than /// `BASE_TIME` are used with these conversion functions, they will fail. /// To avoid that, we make sure that this sets the base time using the first value /// it sees if it is in the past. If it is not, then use `Instant::now()` instead. pub fn baseline(t: Instant) -> Self { let now = Instant::now(); let prnow = unsafe { PR_Now() }; if now <= t { // `t` is in the future, just use `now`. Self { instant: now, prtime: prnow, } } else { let elapsed = Interval::from(now.duration_since(now)); // An error from these unwrap functions would require // ridiculously long application running time. let prelapsed: PRTime = elapsed.try_into().unwrap(); Self { instant: t, prtime: prnow.checked_sub(prelapsed).unwrap(), } } } } static BASE_TIME: OnceCell = OnceCell::new(); fn get_base() -> &'static TimeZero { BASE_TIME.get_or_init(|| TimeZero { instant: Instant::now(), prtime: unsafe { PR_Now() }, }) } pub fn init() { let _ = get_base(); } /// Time wraps Instant and provides conversion functions into `PRTime`. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Time { t: Instant, } impl Deref for Time { type Target = Instant; fn deref(&self) -> &Self::Target { &self.t } } impl From for Time { /// Convert from an Instant into a Time. fn from(t: Instant) -> Self { // Call `TimeZero::baseline(t)` so that time zero can be set. BASE_TIME.get_or_init(|| TimeZero::baseline(t)); Self { t } } } impl TryFrom for Time { type Error = Error; fn try_from(prtime: PRTime) -> Res { let base = get_base(); if let Some(delta) = prtime.checked_sub(base.prtime) { let d = Duration::from_micros(delta.try_into()?); base.instant .checked_add(d) .map_or(Err(Error::TimeTravelError), |t| Ok(Self { t })) } else { Err(Error::TimeTravelError) } } } impl TryInto for Time { type Error = Error; fn try_into(self) -> Res { let base = get_base(); let delta = self .t .checked_duration_since(base.instant) .ok_or(Error::TimeTravelError)?; if let Ok(d) = PRTime::try_from(delta.as_micros()) { d.checked_add(base.prtime).ok_or(Error::TimeTravelError) } else { Err(Error::TimeTravelError) } } } impl From