// 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 std::{ boxed::Box, convert::{TryFrom, TryInto}, ops::Deref, os::raw::c_void, pin::Pin, time::{Duration, Instant}, }; use crate::{ agentio::as_c_void, err::{Error, Res}, once::OnceResult, ssl::{PRFileDesc, SSLTimeFunc}, }; include!(concat!(env!("OUT_DIR"), "/nspr_time.rs")); 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 mut BASE_TIME: OnceResult = OnceResult::new(); fn get_base() -> &'static TimeZero { let f = || TimeZero { instant: Instant::now(), prtime: unsafe { PR_Now() }, }; unsafe { BASE_TIME.call_once(f) } } pub(crate) fn init() { _ = 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. let f = || TimeZero::baseline(t); _ = unsafe { BASE_TIME.call_once(f) }; 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