// 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::{ ops::Deref, os::raw::c_void, pin::Pin, sync::OnceLock, time::{Duration, Instant}, }; use crate::{ agentio::as_c_void, err::{Error, Res}, 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 BASE_TIME: OnceLock = OnceLock::new(); fn get_base() -> &'static TimeZero { BASE_TIME.get_or_init(|| TimeZero { instant: Instant::now(), prtime: unsafe { PR_Now() }, }) } 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 { // Initialize `BASE_TIME` using `TimeZero::baseline(t)`. 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(); let delta = prtime .checked_sub(base.prtime) .ok_or(Error::TimeTravelError)?; let d = Duration::from_micros(u64::try_from(delta.abs())?); let t = if delta >= 0 { base.instant.checked_add(d) } else { base.instant.checked_sub(d) }; let t = t.ok_or(Error::TimeTravelError)?; Ok(Self { t }) } } impl TryInto for Time { type Error = Error; fn try_into(self) -> Res { let base = get_base(); if let Some(delta) = self.t.checked_duration_since(base.instant) { if let Ok(d) = PRTime::try_from(delta.as_micros()) { d.checked_add(base.prtime).ok_or(Error::TimeTravelError) } else { Err(Error::TimeTravelError) } } else { // Try to go backwards from the base time. let backwards = base.instant - self.t; // infallible if let Ok(d) = PRTime::try_from(backwards.as_micros()) { base.prtime.checked_sub(d).ok_or(Error::TimeTravelError) } else { Err(Error::TimeTravelError) } } } } impl From