use std::time::Instant;
use crate::ffi::models::Limits;
use crate::ffi::{Buffer, Slice};
use crate::hsts::HSTSStore;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct HSTSPolicy {
pub max_age: i64,
pub include_subdomains: bool,
pub preload: bool,
}
impl HSTSPolicy {
pub fn parse(&self) -> crate::hsts::HSTSPolicy {
crate::hsts::HSTSPolicy {
max_age: self.max_age,
include_subdomains: self.include_subdomains,
preload: self.preload,
}
}
pub fn build(policy: &crate::hsts::HSTSPolicy) -> Self {
Self {
max_age: policy.max_age,
include_subdomains: policy.include_subdomains,
preload: policy.preload,
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_hsts_policy_parse(value: *const u8, value_len: usize, out: *mut HSTSPolicy) -> bool {
if out.is_null() {
return false;
}
let Some(value) = (unsafe { Slice::borrow_text(value, value_len) }) else {
return false;
};
match crate::hsts::HSTSPolicy::parse(value) {
Some(policy) => {
unsafe { *out = HSTSPolicy::build(&policy) };
true
}
None => false,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_hsts_policy_build(policy: *const HSTSPolicy) -> Buffer {
match unsafe { policy.as_ref() } {
Some(policy) => Buffer::new(policy.parse().build().into_bytes()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_hsts_store_new(limits: *const Limits) -> *mut HSTSStore {
Box::into_raw(Box::new(HSTSStore::new().with_limits(unsafe { Limits::or_default(limits) })))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_hsts_store_free(store: *mut HSTSStore) {
if !store.is_null() {
drop(unsafe { Box::from_raw(store) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_hsts_store_learn(store: *const HSTSStore, host: *const u8, host_len: usize, header: *const u8, header_len: usize, secure: bool) -> bool {
let (Some(store), Some(host), Some(header)) = (unsafe { store.as_ref() }, unsafe { Slice::borrow_text(host, host_len) }, unsafe { Slice::borrow_text(header, header_len) })
else {
return false;
};
store.learn(host, header, secure, Instant::now());
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_hsts_store_secure(store: *const HSTSStore, host: *const u8, host_len: usize) -> bool {
let (Some(store), Some(host)) = (unsafe { store.as_ref() }, unsafe { Slice::borrow_text(host, host_len) }) else {
return false;
};
store.secure(host, Instant::now())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_hsts_store_prune(store: *const HSTSStore) {
if let Some(store) = unsafe { store.as_ref() } {
store.prune(Instant::now());
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_hsts_default_max_entries() -> u32 {
crate::hsts::HSTSLimits::default().max_hsts_entries
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_hsts_policy_new(max_age: i64) -> HSTSPolicy {
HSTSPolicy::build(&crate::hsts::HSTSPolicy::new(max_age))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_hsts_normalize(host: *const u8, host_len: usize) -> Buffer {
match unsafe { Slice::borrow_text(host, host_len) }.and_then(HSTSStore::normalize) {
Some(host) => Buffer::new(host.into_bytes()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_hsts_store_len(store: *const HSTSStore) -> usize {
match unsafe { store.as_ref() } {
Some(store) => crate::helpers::sync::Lock::on(&store.entries).len(),
None => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_hsts_store_max_entries(store: *const HSTSStore) -> u32 {
unsafe { store.as_ref() }.map_or(0, |store| store.limits.max_hsts_entries)
}